diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 1ed9e1f8f..1baf20aee 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -31,24 +31,17 @@ import ( "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/guardian" - "github.com/cgrates/cgrates/scheduler" "github.com/cgrates/cgrates/utils" ) -// SchedulerGeter used to avoid ciclic dependency -type SchedulerGeter interface { - GetScheduler() *scheduler.Scheduler -} - type APIerSv1 struct { - StorDb engine.LoadStorage // we should consider keeping only one of StorDB type - CdrDb engine.CdrStorage - DataManager *engine.DataManager - Config *config.CGRConfig - Responder *engine.Responder - SchedulerService SchedulerGeter // Need to have them capitalize so we can export in V2 - FilterS *engine.FilterS //Used for CDR Exporter - ConnMgr *engine.ConnManager + StorDb engine.LoadStorage // we should consider keeping only one of StorDB type + CdrDb engine.CdrStorage + DataManager *engine.DataManager + Config *config.CGRConfig + Responder *engine.Responder + FilterS *engine.FilterS //Used for CDR Exporter + ConnMgr *engine.ConnManager StorDBChan chan engine.StorDB ResponderChan chan *engine.Responder @@ -261,7 +254,7 @@ func (apierSv1 *APIerSv1) LoadDestination(attrs *AttrLoadDestination, reply *str } dbReader, err := engine.NewTpReader(apierSv1.DataManager.DataDB(), apierSv1.StorDb, attrs.TPid, apierSv1.Config.GeneralCfg().DefaultTimezone, apierSv1.Config.ApierCfg().CachesConns, - apierSv1.Config.ApierCfg().SchedulerConns, + apierSv1.Config.ApierCfg().ActionConns, apierSv1.Config.DataDbCfg().Type == utils.INTERNAL) if err != nil { return utils.NewErrServerError(err) @@ -293,7 +286,7 @@ func (apierSv1 *APIerSv1) LoadRatingPlan(attrs *AttrLoadRatingPlan, reply *strin } dbReader, err := engine.NewTpReader(apierSv1.DataManager.DataDB(), apierSv1.StorDb, attrs.TPid, apierSv1.Config.GeneralCfg().DefaultTimezone, - apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().SchedulerConns, + apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().ActionConns, apierSv1.Config.DataDbCfg().Type == utils.INTERNAL) if err != nil { return utils.NewErrServerError(err) @@ -317,7 +310,7 @@ func (apierSv1 *APIerSv1) LoadRatingProfile(attrs *utils.TPRatingProfile, reply } dbReader, err := engine.NewTpReader(apierSv1.DataManager.DataDB(), apierSv1.StorDb, attrs.TPid, apierSv1.Config.GeneralCfg().DefaultTimezone, - apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().SchedulerConns, + apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().ActionConns, apierSv1.Config.DataDbCfg().Type == utils.INTERNAL) if err != nil { return utils.NewErrServerError(err) @@ -347,7 +340,7 @@ func (apierSv1 *APIerSv1) LoadSharedGroup(attrs *AttrLoadSharedGroup, reply *str } dbReader, err := engine.NewTpReader(apierSv1.DataManager.DataDB(), apierSv1.StorDb, attrs.TPid, apierSv1.Config.GeneralCfg().DefaultTimezone, - apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().SchedulerConns, + apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().ActionConns, apierSv1.Config.DataDbCfg().Type == utils.INTERNAL) if err != nil { return utils.NewErrServerError(err) @@ -374,7 +367,7 @@ func (apierSv1 *APIerSv1) LoadTariffPlanFromStorDb(attrs *AttrLoadTpFromStorDb, } dbReader, err := engine.NewTpReader(apierSv1.DataManager.DataDB(), apierSv1.StorDb, attrs.TPid, apierSv1.Config.GeneralCfg().DefaultTimezone, - apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().SchedulerConns, + apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().ActionConns, apierSv1.Config.DataDbCfg().Type == utils.INTERNAL) if err != nil { return utils.NewErrServerError(err) @@ -405,7 +398,7 @@ func (apierSv1 *APIerSv1) LoadTariffPlanFromStorDb(attrs *AttrLoadTpFromStorDb, if err := dbReader.ReloadCache(caching, true, attrs.APIOpts); err != nil { return utils.NewErrServerError(err) } - if len(apierSv1.Config.ApierCfg().SchedulerConns) != 0 { + if len(apierSv1.Config.ApierCfg().ActionConns) != 0 { utils.Logger.Info("APIerSv1.LoadTariffPlanFromStorDb, reloading scheduler.") if err := dbReader.ReloadScheduler(true); err != nil { return utils.NewErrServerError(err) @@ -579,130 +572,10 @@ type V1TPAction struct { Weight float64 // Action's weight } -func (apierSv1 *APIerSv1) SetActions(attrs *V1AttrSetActions, reply *string) (err error) { - if missing := utils.MissingStructFields(attrs, []string{"ActionsId", "Actions"}); len(missing) != 0 { - return utils.NewErrMandatoryIeMissing(missing...) - } - for _, action := range attrs.Actions { - requiredFields := []string{"Identifier", "Weight"} - if action.BalanceType != utils.EmptyString { // Add some inter-dependent parameters - if balanceType then we are not talking about simply calling actions - requiredFields = append(requiredFields, "Units") - } - if missing := utils.MissingStructFields(action, requiredFields); len(missing) != 0 { - return fmt.Errorf("%s:Action:%s:%v", utils.ErrMandatoryIeMissing.Error(), action.Identifier, missing) - } - } - if !attrs.Overwrite { - if exists, err := apierSv1.DataManager.HasData(utils.ActionPrefix, attrs.ActionsId, ""); err != nil { - return utils.NewErrServerError(err) - } else if exists { - return utils.ErrExists - } - } - storeActions := make(engine.Actions, len(attrs.Actions)) - for idx, apiAct := range attrs.Actions { - var blocker *bool - if apiAct.BalanceBlocker != utils.EmptyString { - if x, err := strconv.ParseBool(apiAct.BalanceBlocker); err == nil { - blocker = &x - } else { - return err - } - } - - var disabled *bool - if apiAct.BalanceDisabled != utils.EmptyString { - if x, err := strconv.ParseBool(apiAct.BalanceDisabled); err == nil { - disabled = &x - } else { - return err - } - } - a := &engine.Action{ - Id: attrs.ActionsId, - ActionType: apiAct.Identifier, - Weight: apiAct.Weight, - ExpirationString: apiAct.ExpiryTime, - ExtraParameters: apiAct.ExtraParameters, - Filter: apiAct.Filter, - Balance: &engine.BalanceFilter{ // TODO: update this part - Uuid: utils.StringPointer(apiAct.BalanceUuid), - ID: utils.StringPointer(apiAct.BalanceId), - Type: utils.StringPointer(apiAct.BalanceType), - Value: &utils.ValueFormula{Static: apiAct.Units}, - Weight: apiAct.BalanceWeight, - DestinationIDs: utils.StringMapPointer(utils.ParseStringMap(apiAct.DestinationIds)), - RatingSubject: utils.StringPointer(apiAct.RatingSubject), - SharedGroups: utils.StringMapPointer(utils.ParseStringMap(apiAct.SharedGroups)), - Categories: utils.StringMapPointer(utils.ParseStringMap(apiAct.Categories)), - TimingIDs: utils.StringMapPointer(utils.ParseStringMap(apiAct.TimingTags)), - Blocker: blocker, - Disabled: disabled, - }, - } - storeActions[idx] = a - } - if err := apierSv1.DataManager.SetActions(attrs.ActionsId, storeActions, utils.NonTransactional); err != nil { - return utils.NewErrServerError(err) - } - //CacheReload - if err := apierSv1.ConnMgr.Call(apierSv1.Config.ApierCfg().CachesConns, nil, - utils.CacheSv1ReloadCache, utils.AttrReloadCacheWithAPIOpts{ - ArgsCache: map[string][]string{utils.ActionIDs: {attrs.ActionsId}}, - }, reply); err != nil { - return err - } - //generate a loadID for CacheActions and store it in database - if err := apierSv1.DataManager.SetLoadIDs(map[string]int64{utils.CacheActions: time.Now().UnixNano()}); err != nil { - return utils.APIErrorHandler(err) - } - *reply = utils.OK - return nil -} - -// Retrieves actions attached to specific ActionsId within cache -func (apierSv1 *APIerSv1) GetActions(actsId *string, reply *[]*utils.TPAction) error { - if len(*actsId) == 0 { - return fmt.Errorf("%s ActionsId: %s", utils.ErrMandatoryIeMissing.Error(), *actsId) - } - acts := make([]*utils.TPAction, 0) - engActs, err := apierSv1.DataManager.GetActions(*actsId, false, utils.NonTransactional) - if err != nil { - return utils.NewErrServerError(err) - } - for _, engAct := range engActs { - act := &utils.TPAction{ - Identifier: engAct.ActionType, - ExpiryTime: engAct.ExpirationString, - ExtraParameters: engAct.ExtraParameters, - Filter: engAct.Filter, - Weight: engAct.Weight, - } - bf := engAct.Balance - if bf != nil { - act.BalanceType = bf.GetType() - act.Units = strconv.FormatFloat(bf.GetValue(), 'f', -1, 64) - act.DestinationIds = bf.GetDestinationIDs().String() - act.RatingSubject = bf.GetRatingSubject() - act.SharedGroups = bf.GetSharedGroups().String() - act.BalanceWeight = strconv.FormatFloat(bf.GetWeight(), 'f', -1, 64) - act.TimingTags = bf.GetTimingIDs().String() - act.BalanceId = bf.GetID() - act.Categories = bf.GetCategories().String() - act.BalanceBlocker = strconv.FormatBool(bf.GetBlocker()) - act.BalanceDisabled = strconv.FormatBool(bf.GetDisabled()) - } - acts = append(acts, act) - } - *reply = acts - return nil -} - type AttrSetActionPlan struct { - Id string // Profile id - ActionPlan []*AttrActionPlan // Set of actions this Actions profile will perform - Overwrite bool // If previously defined, will be overwritten - ReloadScheduler bool // Enables automatic reload of the scheduler (eg: useful when adding a single action timing) + Id string // Profile id + ActionPlan []*AttrActionPlan // Set of actions this Actions profile will perform + Overwrite bool // If previously defined, will be overwritten } type AttrActionPlan struct { @@ -818,13 +691,6 @@ func (apierSv1 *APIerSv1) SetActionPlan(attrs *AttrSetActionPlan, reply *string) if err != nil { return err } - if attrs.ReloadScheduler { - sched := apierSv1.SchedulerService.GetScheduler() - if sched == nil { - return errors.New(utils.SchedulerNotRunningCaps) - } - sched.Reload() - } //generate a loadID for CacheActionPlans and store it in database if err := apierSv1.DataManager.SetLoadIDs(map[string]int64{utils.CacheActionPlans: time.Now().UnixNano()}); err != nil { return utils.APIErrorHandler(err) @@ -1014,7 +880,7 @@ func (apierSv1 *APIerSv1) LoadAccountActions(attrs *utils.TPAccountActions, repl } dbReader, err := engine.NewTpReader(apierSv1.DataManager.DataDB(), apierSv1.StorDb, attrs.TPid, apierSv1.Config.GeneralCfg().DefaultTimezone, - apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().SchedulerConns, + apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().ActionConns, apierSv1.Config.DataDbCfg().Type == utils.INTERNAL) if err != nil { return utils.NewErrServerError(err) @@ -1026,10 +892,6 @@ func (apierSv1 *APIerSv1) LoadAccountActions(attrs *utils.TPAccountActions, repl } // ToDo: Get the action keys loaded by dbReader so we reload only these in cache // Need to do it before scheduler otherwise actions to run will be unknown - sched := apierSv1.SchedulerService.GetScheduler() - if sched != nil { - sched.Reload() - } *reply = utils.OK return nil } @@ -1053,7 +915,7 @@ func (apierSv1 *APIerSv1) LoadTariffPlanFromFolder(attrs *utils.AttrLoadTpFromFo loader, err := engine.NewTpReader(apierSv1.DataManager.DataDB(), engine.NewFileCSVStorage(utils.CSVSep, attrs.FolderPath), "", apierSv1.Config.GeneralCfg().DefaultTimezone, - apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().SchedulerConns, + apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().ActionConns, apierSv1.Config.DataDbCfg().Type == utils.INTERNAL) if err != nil { return utils.NewErrServerError(err) @@ -1087,7 +949,7 @@ func (apierSv1 *APIerSv1) LoadTariffPlanFromFolder(attrs *utils.AttrLoadTpFromFo if err := loader.ReloadCache(caching, true, attrs.APIOpts); err != nil { return utils.NewErrServerError(err) } - if len(apierSv1.Config.ApierCfg().SchedulerConns) != 0 { + if len(apierSv1.Config.ApierCfg().ActionConns) != 0 { utils.Logger.Info("APIerSv1.LoadTariffPlanFromFolder, reloading scheduler.") if err := loader.ReloadScheduler(true); err != nil { return utils.NewErrServerError(err) @@ -1119,7 +981,7 @@ func (apierSv1 *APIerSv1) RemoveTPFromFolder(attrs *utils.AttrLoadTpFromFolder, // create the TpReader loader, err := engine.NewTpReader(apierSv1.DataManager.DataDB(), engine.NewFileCSVStorage(utils.CSVSep, attrs.FolderPath), "", apierSv1.Config.GeneralCfg().DefaultTimezone, - apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().SchedulerConns, + apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().ActionConns, apierSv1.Config.DataDbCfg().Type == utils.INTERNAL) if err != nil { return utils.NewErrServerError(err) @@ -1153,7 +1015,7 @@ func (apierSv1 *APIerSv1) RemoveTPFromFolder(attrs *utils.AttrLoadTpFromFolder, if err := loader.ReloadCache(caching, true, attrs.APIOpts); err != nil { return utils.NewErrServerError(err) } - if len(apierSv1.Config.ApierCfg().SchedulerConns) != 0 { + if len(apierSv1.Config.ApierCfg().ActionConns) != 0 { utils.Logger.Info("APIerSv1.RemoveTPFromFolder, reloading scheduler.") if err := loader.ReloadScheduler(true); err != nil { return utils.NewErrServerError(err) @@ -1173,7 +1035,7 @@ func (apierSv1 *APIerSv1) RemoveTPFromStorDB(attrs *AttrLoadTpFromStorDb, reply } dbReader, err := engine.NewTpReader(apierSv1.DataManager.DataDB(), apierSv1.StorDb, attrs.TPid, apierSv1.Config.GeneralCfg().DefaultTimezone, - apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().SchedulerConns, + apierSv1.Config.ApierCfg().CachesConns, apierSv1.Config.ApierCfg().ActionConns, apierSv1.Config.DataDbCfg().Type == utils.INTERNAL) if err != nil { return utils.NewErrServerError(err) @@ -1205,7 +1067,7 @@ func (apierSv1 *APIerSv1) RemoveTPFromStorDB(attrs *AttrLoadTpFromStorDb, reply if err := dbReader.ReloadCache(caching, true, attrs.APIOpts); err != nil { return utils.NewErrServerError(err) } - if len(apierSv1.Config.ApierCfg().SchedulerConns) != 0 { + if len(apierSv1.Config.ApierCfg().ActionConns) != 0 { utils.Logger.Info("APIerSv1.RemoveTPFromStorDB, reloading scheduler.") if err := dbReader.ReloadScheduler(true); err != nil { return utils.NewErrServerError(err) diff --git a/apier/v1/apier2_it_test.go b/apier/v1/apier2_it_test.go index e3417bd7d..1eef8a53b 100644 --- a/apier/v1/apier2_it_test.go +++ b/apier/v1/apier2_it_test.go @@ -28,8 +28,6 @@ import ( "testing" "time" - "github.com/cgrates/cgrates/scheduler" - "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/dispatchers" "github.com/cgrates/cgrates/engine" @@ -330,7 +328,6 @@ func testAPIerSetActionPlanDfltTime(t *testing.T) { Weight: 20.0, }, }, - ReloadScheduler: true, } if err := apierRPC.Call(utils.APIerSv1SetActionPlan, &hourlyAP, &reply1); err != nil { t.Error("Got error on APIerSv1.SetActionPlan: ", err.Error()) @@ -346,7 +343,6 @@ func testAPIerSetActionPlanDfltTime(t *testing.T) { Weight: 20.0, }, }, - ReloadScheduler: true, } if err := apierRPC.Call(utils.APIerSv1SetActionPlan, &dailyAP, &reply1); err != nil { t.Error("Got error on APIerSv1.SetActionPlan: ", err.Error()) @@ -362,7 +358,6 @@ func testAPIerSetActionPlanDfltTime(t *testing.T) { Weight: 20.0, }, }, - ReloadScheduler: true, } if err := apierRPC.Call(utils.APIerSv1SetActionPlan, &weeklyAP, &reply1); err != nil { t.Error("Got error on APIerSv1.SetActionPlan: ", err.Error()) @@ -378,50 +373,12 @@ func testAPIerSetActionPlanDfltTime(t *testing.T) { Weight: 20.0, }, }, - ReloadScheduler: true, } if err := apierRPC.Call(utils.APIerSv1SetActionPlan, &monthlyAP, &reply1); err != nil { t.Error("Got error on APIerSv1.SetActionPlan: ", err.Error()) } else if reply1 != utils.OK { t.Errorf("Calling APIerSv1.SetActionPlan received: %s", reply1) } - var rply []*scheduler.ScheduledAction - if err := apierRPC.Call(utils.APIerSv1GetScheduledActions, - scheduler.ArgsGetScheduledActions{}, &rply); err != nil { - t.Error("Unexpected error: ", err) - } else { - for _, schedAct := range rply { - switch schedAct.ActionPlanID { - case "AP_WEEKLY": - t1 := time.Now().AddDate(0, 0, 7) - if schedAct.NextRunTime.Before(t1.Add(-2*time.Second)) || - schedAct.NextRunTime.After(t1.Add(time.Second)) { - t.Errorf("Expected the nextRuntime to be after 1 week,but received: <%+v>", utils.ToJSON(schedAct)) - } - case "AP_DAILY": - t1 := time.Now().AddDate(0, 0, 1) - if schedAct.NextRunTime.Before(t1.Add(-2*time.Second)) || - schedAct.NextRunTime.After(t1.Add(time.Second)) { - t.Errorf("Expected the nextRuntime to be after 1 day,but received: <%+v>", utils.ToJSON(schedAct)) - } - case "AP_HOURLY": - if schedAct.NextRunTime.Before(time.Now().Add(59*time.Minute+58*time.Second)) || - schedAct.NextRunTime.After(time.Now().Add(time.Hour+time.Second)) { - t.Errorf("Expected the nextRuntime to be after 1 hour,but received: <%+v>", utils.ToJSON(schedAct)) - } - case "AP_MONTHLY": - // *monthly needs to mach exactly the day - tnow := time.Now() - expected := tnow.AddDate(0, 1, 0) - expected = time.Date(expected.Year(), expected.Month(), tnow.Day(), tnow.Hour(), - tnow.Minute(), tnow.Second(), 0, schedAct.NextRunTime.Location()) - if schedAct.NextRunTime.Before(expected.Add(-time.Second)) || - schedAct.NextRunTime.After(expected.Add(time.Second)) { - t.Errorf("Expected the nextRuntime to be after 1 month,but received: <%+v>", utils.ToJSON(schedAct)) - } - } - } - } } func testAPIerLoadRatingPlan(t *testing.T) { diff --git a/apier/v1/scheduler.go b/apier/v1/scheduler.go deleted file mode 100644 index c168ebdde..000000000 --- a/apier/v1/scheduler.go +++ /dev/null @@ -1,110 +0,0 @@ -/* -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 v1 - -import ( - "errors" - - "github.com/cgrates/cgrates/scheduler" - "github.com/cgrates/cgrates/utils" -) - -/* -[ - { - u'ActionsId': u'BONUS_1', - u'Uuid': u'5b5ba53b40b1d44380cce52379ec5c0d', - u'Weight': 10, - u'Timing': { - u'Timing': { - u'MonthDays': [ - - ], - u'Months': [ - - ], - u'WeekDays': [ - - ], - u'Years': [ - 2013 - ], - u'StartTime': u'11: 00: 00', - u'EndTime': u'' - }, - u'Rating': None, - u'Weight': 0 - }, - u'AccountIds': [ - u'*out: cgrates.org: 1001', - u'*out: cgrates.org: 1002', - u'*out: cgrates.org: 1003', - u'*out: cgrates.org: 1004', - u'*out: cgrates.org: 1005' - ], - u'Id': u'PREPAID_10' - }, - { - u'ActionsId': u'PREPAID_10', - u'Uuid': u'b16ab12740e2e6c380ff7660e8b55528', - u'Weight': 10, - u'Timing': { - u'Timing': { - u'MonthDays': [ - - ], - u'Months': [ - - ], - u'WeekDays': [ - - ], - u'Years': [ - 2013 - ], - u'StartTime': u'11: 00: 00', - u'EndTime': u'' - }, - u'Rating': None, - u'Weight': 0 - }, - u'AccountIds': [ - u'*out: cgrates.org: 1001', - u'*out: cgrates.org: 1002', - u'*out: cgrates.org: 1003', - u'*out: cgrates.org: 1004', - u'*out: cgrates.org: 1005' - ], - u'Id': u'PREPAID_10' - } -] -*/ - -func (apierSv1 *APIerSv1) GetScheduledActions(args *scheduler.ArgsGetScheduledActions, reply *[]*scheduler.ScheduledAction) error { - sched := apierSv1.SchedulerService.GetScheduler() - if sched == nil { - return errors.New(utils.SchedulerNotRunningCaps) - } - rpl := sched.GetScheduledActions(*args) - if len(rpl) == 0 { - return utils.ErrNotFound - } - *reply = rpl - return nil -} diff --git a/apier/v1/schedulers.go b/apier/v1/schedulers.go deleted file mode 100644 index 7cf138ef5..000000000 --- a/apier/v1/schedulers.go +++ /dev/null @@ -1,182 +0,0 @@ -/* -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 v1 - -import ( - "fmt" - "sort" - "time" - - "github.com/cgrates/cgrates/config" - "github.com/cgrates/cgrates/engine" - "github.com/cgrates/cgrates/utils" -) - -// NewSchedulerSv1 retuns the API for SchedulerS -func NewSchedulerSv1(cgrcfg *config.CGRConfig, dm *engine.DataManager) *SchedulerSv1 { - return &SchedulerSv1{cgrcfg: cgrcfg, dm: dm} -} - -// SchedulerSv1 is the RPC object implementing scheduler APIs -type SchedulerSv1 struct { - cgrcfg *config.CGRConfig - dm *engine.DataManager -} - -// Reload reloads scheduler instructions -func (schdSv1 *SchedulerSv1) Reload(arg *utils.CGREvent, reply *string) error { - schdSv1.cgrcfg.GetReloadChan(config.SCHEDULER_JSN) <- struct{}{} - *reply = utils.OK - return nil -} - -// ExecuteActions execute an actionPlan or multiple actionsPlans between a time interval -func (schdSv1 *SchedulerSv1) ExecuteActions(attr *utils.AttrsExecuteActions, reply *string) error { - if attr.ActionPlanID != utils.EmptyString { // execute by ActionPlanID - apl, err := schdSv1.dm.GetActionPlan(attr.ActionPlanID, false, utils.NonTransactional) - if err != nil { - *reply = err.Error() - return err - } - if apl != nil { - // order by weight - engine.ActionTimingWeightOnlyPriorityList(apl.ActionTimings).Sort() - for _, at := range apl.ActionTimings { - if at.IsASAP() { - continue - } - - at.SetAccountIDs(apl.AccountIDs) // copy the accounts - at.SetActionPlanID(apl.Id) - err := at.Execute(nil, nil) - if err != nil { - *reply = err.Error() - return err - } - utils.Logger.Info(fmt.Sprintf(" Executing action %s ", at.ActionsID)) - } - } - } - if !attr.TimeStart.IsZero() && !attr.TimeEnd.IsZero() { // execute between two dates - actionPlans, err := schdSv1.dm.GetAllActionPlans() - if err != nil && err != utils.ErrNotFound { - err := fmt.Errorf("cannot get action plans: %v", err) - *reply = err.Error() - return err - } - - // recreate the queue - queue := engine.ActionTimingPriorityList{} - for _, actionPlan := range actionPlans { - for _, at := range actionPlan.ActionTimings { - if at.Timing == nil { - continue - } - if at.IsASAP() { - continue - } - if at.GetNextStartTime(attr.TimeStart).Before(attr.TimeStart) { - // the task is obsolete, do not add it to the queue - continue - } - at.SetAccountIDs(actionPlan.AccountIDs) // copy the accounts - at.SetActionPlanID(actionPlan.Id) - at.ResetStartTimeCache() - queue = append(queue, at) - } - } - sort.Sort(queue) - // start playback execution loop - current := attr.TimeStart - for len(queue) > 0 && current.Before(attr.TimeEnd) { - a0 := queue[0] - current = a0.GetNextStartTime(current) - if current.Before(attr.TimeEnd) || current.Equal(attr.TimeEnd) { - utils.Logger.Info(fmt.Sprintf(" Executing action %s for time %v", a0.ActionsID, current)) - err := a0.Execute(nil, nil) - if err != nil { - *reply = err.Error() - return err - } - // if after execute the next start time is in the past then - // do not add it to the queue - a0.ResetStartTimeCache() - current = current.Add(time.Second) - start := a0.GetNextStartTime(current) - if start.Before(current) || start.After(attr.TimeEnd) { - queue = queue[1:] - } else { - queue = append(queue, a0) - queue = queue[1:] - sort.Sort(queue) - } - } - } - } - *reply = utils.OK - return nil -} - -// ExecuteActionPlans execute multiple actionPlans one by one -func (schdSv1 *SchedulerSv1) ExecuteActionPlans(attr *utils.AttrsExecuteActionPlans, reply *string) (err error) { - // try get account - // if not exist set in DM - accID := utils.ConcatenatedKey(attr.Tenant, attr.AccountID) - if _, err = schdSv1.dm.GetAccount(accID); err != nil { - // create account if does not exist - account := &engine.Account{ - ID: accID, - } - if err = schdSv1.dm.SetAccount(account); err != nil { - return - } - } - for _, apID := range attr.ActionPlanIDs { - apl, err := schdSv1.dm.GetActionPlan(apID, false, utils.NonTransactional) - if err != nil { - *reply = err.Error() - return err - } - if apl != nil { - // order by weight - engine.ActionTimingWeightOnlyPriorityList(apl.ActionTimings).Sort() - for _, at := range apl.ActionTimings { - at.SetAccountIDs(utils.NewStringMap(accID)) - err := at.Execute(nil, nil) - if err != nil { - *reply = err.Error() - return err - } - utils.Logger.Info(fmt.Sprintf(" Executing action %s ", at.ActionsID)) - } - } - } - - *reply = utils.OK - return nil -} - -// Ping returns Pong -func (schdSv1 *SchedulerSv1) Ping(ign *utils.CGREvent, reply *string) error { - *reply = utils.Pong - return nil -} - -// Call implements rpcclient.ClientConnector interface for internal RPC -func (schdSv1 *SchedulerSv1) Call(serviceMethod string, - args interface{}, reply interface{}) error { - return utils.APIerRPCCall(schdSv1, serviceMethod, args, reply) -} diff --git a/apier/v1/schedulers_it_test.go b/apier/v1/schedulers_it_test.go deleted file mode 100644 index 8582efbe0..000000000 --- a/apier/v1/schedulers_it_test.go +++ /dev/null @@ -1,332 +0,0 @@ -// +build integration - -/* -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 v1 - -import ( - "net/rpc" - "path" - "testing" - "time" - - "github.com/cgrates/cgrates/utils" - - "github.com/cgrates/cgrates/engine" - - "github.com/cgrates/cgrates/config" -) - -var ( - schedCfgPath string - schedCfg *config.CGRConfig - schedRpc *rpc.Client - schedConfDIR string //run tests for specific configuration -) - -var sTestsSchedFiltered = []func(t *testing.T){ - testSchedLoadConfig, - testSchedInitDataDb, - testSchedResetStorDb, - testSchedStartEngine, - testSchedRpcConn, - testSchedFromFolder, - testSchedVeifyAllAccounts, - testSchedVeifyAccount1001, - testSchedVeifyAccount1002and1003, - testSchedExecuteAction, - testSchedStopEngine, -} - -//TestSchedWithoutFilters will execute action for all accounts -func TestSchedWithoutFilters(t *testing.T) { - switch *dbType { - case utils.MetaInternal: - schedConfDIR = "tutinternal" - case utils.MetaMySQL: - schedConfDIR = "tutmysql" - case utils.MetaMongo: - schedConfDIR = "tutmongo" - case utils.MetaPostgres: - t.SkipNow() - default: - t.Fatal("Unknown Database type") - } - - for _, stest := range sTestsSchedFiltered { - t.Run(schedConfDIR, stest) - } -} - -//TestSchedWithFiltersSingleAccount will execute actions only for account 1001 -func TestSchedWithFiltersSingleAccount(t *testing.T) { - switch *dbType { - case utils.MetaInternal: - schedConfDIR = "filtered_scheduler_internal" - case utils.MetaMySQL: - schedConfDIR = "filtered_scheduler_mysql" - case utils.MetaMongo: - schedConfDIR = "filtered_scheduler_mongo" - case utils.MetaPostgres: - t.SkipNow() - default: - t.Fatal("Unknown Database type") - } - for _, stest := range sTestsSchedFiltered { - t.Run(schedConfDIR, stest) - } -} - -//TestSchedWithFilters2 will execute actions for accounts 1002 and 1003 ( 1001 will be skiped ) -func TestSchedWithFilters2(t *testing.T) { - switch *dbType { - case utils.MetaInternal: - schedConfDIR = "filtered_scheduler2_internal" - case utils.MetaMySQL: - schedConfDIR = "filtered_scheduler2_mysql" - case utils.MetaMongo: - schedConfDIR = "filtered_scheduler2_mongo" - case utils.MetaPostgres: - t.SkipNow() - default: - t.Fatal("Unknown Database type") - } - for _, stest := range sTestsSchedFiltered { - t.Run(schedConfDIR, stest) - } -} - -func testSchedLoadConfig(t *testing.T) { - var err error - schedCfgPath = path.Join(*dataDir, "conf", "samples", schedConfDIR) - if schedCfg, err = config.NewCGRConfigFromPath(schedCfgPath); err != nil { - t.Error(err) - } -} - -func testSchedInitDataDb(t *testing.T) { - if err := engine.InitDataDb(schedCfg); err != nil { - t.Fatal(err) - } -} - -func testSchedResetStorDb(t *testing.T) { - if err := engine.InitStorDb(schedCfg); err != nil { - t.Fatal(err) - } -} - -func testSchedStartEngine(t *testing.T) { - if _, err := engine.StopStartEngine(schedCfgPath, *waitRater); err != nil { - t.Fatal(err) - } -} - -func testSchedRpcConn(t *testing.T) { - var err error - schedRpc, err = newRPCClient(schedCfg.ListenCfg()) // We connect over JSON so we can also troubleshoot if needed - if err != nil { - t.Fatal("Could not connect to rater: ", err.Error()) - } -} - -func testSchedFromFolder(t *testing.T) { - var reply string - attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial")} - if err := schedRpc.Call(utils.APIerSv1LoadTariffPlanFromFolder, attrs, &reply); err != nil { - t.Error(err) - } - time.Sleep(100 * time.Millisecond) -} - -func testSchedVeifyAllAccounts(t *testing.T) { - if !(schedConfDIR == "tutinternal" || schedConfDIR == "tutmysql" || schedConfDIR == "tutmongo") { - t.SkipNow() - } - - var acnt *engine.Account - attrs := &utils.AttrGetAccount{ - Tenant: "cgrates.org", - Account: "1001", - } - if err := schedRpc.Call(utils.APIerSv2GetAccount, attrs, &acnt); err != nil { - t.Error(err) - } else if rply := acnt.BalanceMap[utils.MetaMonetary].GetTotalValue(); rply != 10 { - t.Errorf("Expecting: %v, received: %v", - 10, rply) - } - attrs = &utils.AttrGetAccount{ - Tenant: "cgrates.org", - Account: "1002", - } - if err := schedRpc.Call(utils.APIerSv2GetAccount, attrs, &acnt); err != nil { - t.Error(err) - } else if rply := acnt.BalanceMap[utils.MetaMonetary].GetTotalValue(); rply != 10 { - t.Errorf("Expecting: %v, received: %v", - 10, rply) - } - attrs = &utils.AttrGetAccount{ - Tenant: "cgrates.org", - Account: "1003", - } - if err := schedRpc.Call(utils.APIerSv2GetAccount, attrs, &acnt); err != nil { - t.Error(err) - } else if rply := acnt.BalanceMap[utils.MetaMonetary].GetTotalValue(); rply != 10 { - t.Errorf("Expecting: %v, received: %v", - 10, rply) - } -} - -func testSchedVeifyAccount1001(t *testing.T) { - if !(schedConfDIR == "filtered_scheduler_internal" || schedConfDIR == "filtered_scheduler_mysql" || schedConfDIR == "filtered_scheduler_mongo") { - t.SkipNow() - } - var acnt *engine.Account - attrs := &utils.AttrGetAccount{ - Tenant: "cgrates.org", - Account: "1001", - } - if err := schedRpc.Call(utils.APIerSv2GetAccount, attrs, &acnt); err != nil { - t.Error(err) - } else if rply := acnt.BalanceMap[utils.MetaMonetary].GetTotalValue(); rply != 10 { - t.Errorf("Expecting: %v, received: %v", - 10, rply) - } - - acnt = nil // in case of gob ( it doesn't update the empty fields) - attrs = &utils.AttrGetAccount{ - Tenant: "cgrates.org", - Account: "1002", - } - if err := schedRpc.Call(utils.APIerSv2GetAccount, attrs, &acnt); err != nil { - t.Error(err) - } else if lenBal := len(acnt.BalanceMap[utils.MetaMonetary]); lenBal != 0 { - t.Errorf("Expecting: %v, received: %v", - 0, lenBal) - } - - attrs = &utils.AttrGetAccount{ - Tenant: "cgrates.org", - Account: "1003", - } - if err := schedRpc.Call(utils.APIerSv2GetAccount, attrs, &acnt); err != nil { - t.Error(err) - } else if lenBal := len(acnt.BalanceMap[utils.MetaMonetary]); lenBal != 0 { - t.Errorf("Expecting: %v, received: %v", - 0, lenBal) - } - -} - -func testSchedVeifyAccount1002and1003(t *testing.T) { - if !(schedConfDIR == "filtered_scheduler2_internal" || schedConfDIR == "filtered_scheduler2_mysql" || schedConfDIR == "filtered_scheduler2_mongo") { - t.SkipNow() - } - var acnt *engine.Account - attrs := &utils.AttrGetAccount{ - Tenant: "cgrates.org", - Account: "1001", - } - if err := schedRpc.Call(utils.APIerSv2GetAccount, attrs, &acnt); err != nil { - t.Error(err) - } else if lenBal := len(acnt.BalanceMap[utils.MetaMonetary]); lenBal != 0 { - t.Errorf("Expecting: %v, received: %v", - 0, lenBal) - } - - attrs = &utils.AttrGetAccount{ - Tenant: "cgrates.org", - Account: "1002", - } - if err := schedRpc.Call(utils.APIerSv2GetAccount, attrs, &acnt); err != nil { - t.Error(err) - } else if rply := acnt.BalanceMap[utils.MetaMonetary].GetTotalValue(); rply != 10 { - t.Errorf("Expecting: %v, received: %v", - 10, rply) - } - - attrs = &utils.AttrGetAccount{ - Tenant: "cgrates.org", - Account: "1003", - } - if err := schedRpc.Call(utils.APIerSv2GetAccount, attrs, &acnt); err != nil { - t.Error(err) - } else if rply := acnt.BalanceMap[utils.MetaMonetary].GetTotalValue(); rply != 10 { - t.Errorf("Expecting: %v, received: %v", - 10, rply) - } -} - -func testSchedExecuteAction(t *testing.T) { - if !(schedConfDIR == "tutinternal" || schedConfDIR == "tutmysql" || schedConfDIR == "tutmongo") { - t.SkipNow() - } - // set a new ActionPlan - var reply1 string - if err := schedRpc.Call(utils.APIerSv1SetActionPlan, &AttrSetActionPlan{ - Id: "CustomAP", - ActionPlan: []*AttrActionPlan{ - { - ActionsId: "ACT_TOPUP_RST_10", - Time: utils.MetaHourly, - Weight: 20.0}, - }, - }, &reply1); err != nil { - t.Error("Got error on APIerSv1.SetActionPlan: ", err.Error()) - } else if reply1 != utils.OK { - t.Errorf("Unexpected reply returned: %s", reply1) - } - var reply string - if err := schedRpc.Call(utils.APIerSv1SetAccount, utils.AttrSetAccount{ - Tenant: "cgrates.org", - Account: "CustomAccount", - ActionPlanID: "CustomAP", - }, &reply); err != nil { - t.Fatal(err) - } - - var acnt *engine.Account - attrs := &utils.AttrGetAccount{ - Tenant: "cgrates.org", - Account: "CustomAccount", - } - expected := 0.0 - if err := schedRpc.Call(utils.APIerSv2GetAccount, attrs, &acnt); err != nil { - t.Error(err) - } else if rply := acnt.BalanceMap[utils.MetaMonetary].GetTotalValue(); rply != expected { - t.Errorf("Expecting: %v, received: %v", - expected, rply) - } - - if err := schedRpc.Call(utils.SchedulerSv1ExecuteActions, &utils.AttrsExecuteActions{ActionPlanID: "CustomAP"}, &reply); err != nil { - t.Error(err) - } - expected = 10.0 - if err := schedRpc.Call(utils.APIerSv2GetAccount, attrs, &acnt); err != nil { - t.Error(err) - } else if rply := acnt.BalanceMap[utils.MetaMonetary].GetTotalValue(); rply != expected { - t.Errorf("Expecting: %v, received: %v", - expected, rply) - } -} - -func testSchedStopEngine(t *testing.T) { - if err := engine.KillEngine(*waitRater); err != nil { - t.Error(err) - } -} diff --git a/apier/v2/apier.go b/apier/v2/apier.go index 50bad02f1..195e5e9bc 100644 --- a/apier/v2/apier.go +++ b/apier/v2/apier.go @@ -56,7 +56,7 @@ func (apiv2 *APIerSv2) LoadRatingProfile(attrs *AttrLoadRatingProfile, reply *st } dbReader, err := engine.NewTpReader(apiv2.DataManager.DataDB(), apiv2.StorDb, attrs.TPid, apiv2.Config.GeneralCfg().DefaultTimezone, - apiv2.Config.ApierCfg().CachesConns, apiv2.Config.ApierCfg().SchedulerConns, + apiv2.Config.ApierCfg().CachesConns, apiv2.Config.ApierCfg().ActionConns, apiv2.Config.DataDbCfg().Type == utils.INTERNAL) if err != nil { return utils.NewErrServerError(err) @@ -86,7 +86,7 @@ func (apiv2 *APIerSv2) LoadAccountActions(attrs *AttrLoadAccountActions, reply * } dbReader, err := engine.NewTpReader(apiv2.DataManager.DataDB(), apiv2.StorDb, attrs.TPid, apiv2.Config.GeneralCfg().DefaultTimezone, - apiv2.Config.ApierCfg().CachesConns, apiv2.Config.ApierCfg().SchedulerConns, + apiv2.Config.ApierCfg().CachesConns, apiv2.Config.ApierCfg().ActionConns, apiv2.Config.DataDbCfg().Type == utils.INTERNAL) if err != nil { return utils.NewErrServerError(err) @@ -98,9 +98,9 @@ func (apiv2 *APIerSv2) LoadAccountActions(attrs *AttrLoadAccountActions, reply * }, config.CgrConfig().GeneralCfg().LockingTimeout, attrs.AccountActionsId); err != nil { return utils.NewErrServerError(err) } - sched := apiv2.SchedulerService.GetScheduler() - if sched != nil { - sched.Reload() + acts := apiv2.ActionService.GetAction() + if acts != nil { + //acts.Reload() } *reply = utils.OK return nil @@ -120,7 +120,7 @@ func (apiv2 *APIerSv2) LoadTariffPlanFromFolder(attrs *utils.AttrLoadTpFromFolde } loader, err := engine.NewTpReader(apiv2.DataManager.DataDB(), engine.NewFileCSVStorage(utils.CSVSep, attrs.FolderPath), "", apiv2.Config.GeneralCfg().DefaultTimezone, - apiv2.Config.ApierCfg().CachesConns, apiv2.Config.ApierCfg().SchedulerConns, + apiv2.Config.ApierCfg().CachesConns, apiv2.Config.ApierCfg().ActionConns, apiv2.Config.DataDbCfg().Type == utils.INTERNAL) if err != nil { return utils.NewErrServerError(err) @@ -152,7 +152,7 @@ func (apiv2 *APIerSv2) LoadTariffPlanFromFolder(attrs *utils.AttrLoadTpFromFolde if err := loader.ReloadCache(caching, true, attrs.APIOpts); err != nil { return utils.NewErrServerError(err) } - if len(apiv2.Config.ApierCfg().SchedulerConns) != 0 { + if len(apiv2.Config.ApierCfg().ActionConns) != 0 { utils.Logger.Info("APIerSv2.LoadTariffPlanFromFolder, reloading scheduler.") if err := loader.ReloadScheduler(true); err != nil { return utils.NewErrServerError(err) diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go index 0f072904f..dd1019fb6 100644 --- a/cmd/cgr-engine/cgr-engine.go +++ b/cmd/cgr-engine/cgr-engine.go @@ -644,9 +644,6 @@ func main() { routeS := services.NewRouteService(cfg, dmService, cacheS, filterSChan, server, internalRouteSChan, connManager, anz, srvDep) - schS := services.NewSchedulerService(cfg, dmService, cacheS, filterSChan, - server, internalSchedulerSChan, connManager, anz, srvDep) - rals := services.NewRalService(cfg, cacheS, server, internalRALsChan, internalResponderChan, shdChan, connManager, anz, srvDep) diff --git a/config/apiercfg.go b/config/apiercfg.go index 574869f63..ee4cd91e0 100644 --- a/config/apiercfg.go +++ b/config/apiercfg.go @@ -26,7 +26,7 @@ import ( type ApierCfg struct { Enabled bool CachesConns []string // connections towards Cache - SchedulerConns []string // connections towards Scheduler + ActionConns []string // connections towards Scheduler AttributeSConns []string // connections towards AttributeS EEsConns []string // connections towards EEs } @@ -49,12 +49,12 @@ func (aCfg *ApierCfg) loadFromJSONCfg(jsnCfg *ApierJsonCfg) (err error) { } } if jsnCfg.Scheduler_conns != nil { - aCfg.SchedulerConns = make([]string, len(*jsnCfg.Scheduler_conns)) + aCfg.ActionConns = make([]string, len(*jsnCfg.Scheduler_conns)) for idx, conn := range *jsnCfg.Scheduler_conns { // if we have the connection internal we change the name so we can have internal rpc for each subsystem - aCfg.SchedulerConns[idx] = conn + aCfg.ActionConns[idx] = conn if conn == utils.MetaInternal { - aCfg.SchedulerConns[idx] = utils.ConcatenatedKey(utils.MetaInternal, utils.MetaScheduler) + aCfg.ActionConns[idx] = utils.ConcatenatedKey(utils.MetaInternal, utils.MetaScheduler) } } } @@ -96,9 +96,9 @@ func (aCfg *ApierCfg) AsMapInterface() (initialMap map[string]interface{}) { } initialMap[utils.CachesConnsCfg] = cachesConns } - if aCfg.SchedulerConns != nil { - schedulerConns := make([]string, len(aCfg.SchedulerConns)) - for i, item := range aCfg.SchedulerConns { + if aCfg.ActionConns != nil { + schedulerConns := make([]string, len(aCfg.ActionConns)) + for i, item := range aCfg.ActionConns { schedulerConns[i] = item if item == utils.ConcatenatedKey(utils.MetaInternal, utils.MetaScheduler) { schedulerConns[i] = utils.MetaInternal @@ -140,10 +140,10 @@ func (aCfg ApierCfg) Clone() (cln *ApierCfg) { cln.CachesConns[i] = k } } - if aCfg.SchedulerConns != nil { - cln.SchedulerConns = make([]string, len(aCfg.SchedulerConns)) - for i, k := range aCfg.SchedulerConns { - cln.SchedulerConns[i] = k + if aCfg.ActionConns != nil { + cln.ActionConns = make([]string, len(aCfg.ActionConns)) + for i, k := range aCfg.ActionConns { + cln.ActionConns[i] = k } } if aCfg.AttributeSConns != nil { diff --git a/config/config.go b/config/config.go index cfa99ac9d..29fde3cd5 100644 --- a/config/config.go +++ b/config/config.go @@ -162,7 +162,6 @@ func newCGRConfig(config []byte) (cfg *CGRConfig, err error) { cfg.ralsCfg = new(RalsCfg) cfg.ralsCfg.MaxComputedUsage = make(map[string]time.Duration) cfg.ralsCfg.BalanceRatingSubject = make(map[string]string) - cfg.schedulerCfg = new(SchedulerCfg) cfg.cdrsCfg = new(CdrsCfg) cfg.analyzerSCfg = new(AnalyzerSCfg) cfg.sessionSCfg = new(SessionSCfg) @@ -308,7 +307,6 @@ type CGRConfig struct { httpCfg *HTTPCfg // HTTP config filterSCfg *FilterSCfg // FilterS config ralsCfg *RalsCfg // Rals config - schedulerCfg *SchedulerCfg // Scheduler config cdrsCfg *CdrsCfg // Cdrs config sessionSCfg *SessionSCfg // SessionS config fsAgentCfg *FsAgentCfg // FreeSWITCHAgent config @@ -399,7 +397,7 @@ func (cfg *CGRConfig) loadFromJSONCfg(jsnCfg *CgrJsonCfg) (err error) { cfg.loadRPCConns, cfg.loadGeneralCfg, cfg.loadTemplateSCfg, cfg.loadCacheCfg, cfg.loadListenCfg, cfg.loadHTTPCfg, cfg.loadDataDBCfg, cfg.loadStorDBCfg, - cfg.loadFilterSCfg, cfg.loadRalSCfg, cfg.loadSchedulerCfg, + cfg.loadFilterSCfg, cfg.loadRalSCfg, cfg.loadCdrsCfg, cfg.loadSessionSCfg, cfg.loadFreeswitchAgentCfg, cfg.loadKamAgentCfg, cfg.loadAsteriskAgentCfg, cfg.loadDiameterAgentCfg, cfg.loadRadiusAgentCfg, @@ -539,15 +537,6 @@ func (cfg *CGRConfig) loadRalSCfg(jsnCfg *CgrJsonCfg) (err error) { return cfg.ralsCfg.loadFromJSONCfg(jsnRALsCfg) } -// loadSchedulerCfg loads the Scheduler section of the configuration -func (cfg *CGRConfig) loadSchedulerCfg(jsnCfg *CgrJsonCfg) (err error) { - var jsnSchedCfg *SchedulerJsonCfg - if jsnSchedCfg, err = jsnCfg.SchedulerJsonCfg(); err != nil { - return - } - return cfg.schedulerCfg.loadFromJSONCfg(jsnSchedCfg) -} - // loadCdrsCfg loads the Cdrs section of the configuration func (cfg *CGRConfig) loadCdrsCfg(jsnCfg *CgrJsonCfg) (err error) { var jsnCdrsCfg *CdrsJsonCfg @@ -1029,13 +1018,6 @@ func (cfg *CGRConfig) MigratorCgrCfg() *MigratorCgrCfg { return cfg.migratorCgrCfg } -// SchedulerCfg returns the config for Scheduler -func (cfg *CGRConfig) SchedulerCfg() *SchedulerCfg { - cfg.lks[SCHEDULER_JSN].Lock() - defer cfg.lks[SCHEDULER_JSN].Unlock() - return cfg.schedulerCfg -} - // DataDbCfg returns the config for DataDb func (cfg *CGRConfig) DataDbCfg() *DataDbCfg { cfg.lks[DATADB_JSN].Lock() @@ -1270,7 +1252,6 @@ func (cfg *CGRConfig) getLoadFunctions() map[string]func(*CgrJsonCfg) error { LISTEN_JSN: cfg.loadListenCfg, TlsCfgJson: cfg.loadTLSCgrCfg, HTTP_JSN: cfg.loadHTTPCfg, - SCHEDULER_JSN: cfg.loadSchedulerCfg, CACHE_JSN: cfg.loadCacheCfg, FilterSjsn: cfg.loadFilterSCfg, RALS_JSN: cfg.loadRalSCfg, @@ -1467,7 +1448,7 @@ func (cfg *CGRConfig) loadCfgFromJSONWithLocks(rdr io.Reader, sections []string) // reloadSections sends a signal to the reload channel for the needed sections // the list of sections should be always valid because we load the config first with this list func (cfg *CGRConfig) reloadSections(sections ...string) { - subsystemsThatNeedDataDB := utils.NewStringSet([]string{DATADB_JSN, SCHEDULER_JSN, + subsystemsThatNeedDataDB := utils.NewStringSet([]string{DATADB_JSN, RALS_JSN, CDRS_JSN, SessionSJson, ATTRIBUTE_JSN, ChargerSCfgJson, RESOURCES_JSON, STATS_JSON, THRESHOLDS_JSON, RouteSJson, LoaderJson, DispatcherSJson, RateSJson, ApierS, AccountSCfgJson, @@ -1510,8 +1491,6 @@ func (cfg *CGRConfig) reloadSections(sections ...string) { case CoreSCfgJson: // nothing to reload case HTTP_JSN: cfg.rldChans[HTTP_JSN] <- struct{}{} - case SCHEDULER_JSN: - cfg.rldChans[SCHEDULER_JSN] <- struct{}{} case RALS_JSN: cfg.rldChans[RALS_JSN] <- struct{}{} case CDRS_JSN: @@ -1585,7 +1564,6 @@ func (cfg *CGRConfig) AsMapInterface(separator string) (mp map[string]interface{ HTTP_JSN: cfg.httpCfg.AsMapInterface(), FilterSjsn: cfg.filterSCfg.AsMapInterface(), RALS_JSN: cfg.ralsCfg.AsMapInterface(), - SCHEDULER_JSN: cfg.schedulerCfg.AsMapInterface(), CDRS_JSN: cfg.cdrsCfg.AsMapInterface(), SessionSJson: cfg.sessionSCfg.AsMapInterface(), FreeSWITCHAgentJSN: cfg.fsAgentCfg.AsMapInterface(separator), @@ -1712,8 +1690,6 @@ func (cfg *CGRConfig) V1GetConfig(args *SectionWithAPIOpts, reply *map[string]in mp = cfg.FilterSCfg().AsMapInterface() case RALS_JSN: mp = cfg.RalsCfg().AsMapInterface() - case SCHEDULER_JSN: - mp = cfg.SchedulerCfg().AsMapInterface() case CDRS_JSN: mp = cfg.CdrsCfg().AsMapInterface() case SessionSJson: @@ -1882,8 +1858,6 @@ func (cfg *CGRConfig) V1GetConfigAsJSON(args *SectionWithAPIOpts, reply *string) mp = cfg.FilterSCfg().AsMapInterface() case RALS_JSN: mp = cfg.RalsCfg().AsMapInterface() - case SCHEDULER_JSN: - mp = cfg.SchedulerCfg().AsMapInterface() case CDRS_JSN: mp = cfg.CdrsCfg().AsMapInterface() case SessionSJson: @@ -2019,7 +1993,6 @@ func (cfg *CGRConfig) Clone() (cln *CGRConfig) { httpCfg: cfg.httpCfg.Clone(), filterSCfg: cfg.filterSCfg.Clone(), ralsCfg: cfg.ralsCfg.Clone(), - schedulerCfg: cfg.schedulerCfg.Clone(), cdrsCfg: cfg.cdrsCfg.Clone(), sessionSCfg: cfg.sessionSCfg.Clone(), fsAgentCfg: cfg.fsAgentCfg.Clone(), diff --git a/config/config_defaults.go b/config/config_defaults.go index 622e9b4eb..ff9b18beb 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -224,16 +224,6 @@ const CGRATES_CFG_JSON = ` }, }, - -"schedulers": { - "enabled": false, // start Scheduler service: - "cdrs_conns": [], // connections to CDRs for *cdrlog actions <""|*internal|$rpc_conns_id> - "thresholds_conns": [], // connections to ThresholdS for *reset_threshold action <""|*internal|$rpc_conns_id> - "stats_conns": [], // connections to StatS for *reset_stat_queue action: <""|*internal|$rpc_conns_id> - "filters": [], // only execute actions matching these filters -}, - - "caches":{ "partitions": { "*destinations": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // destination caching diff --git a/config/config_json.go b/config/config_json.go index a869c6d14..0480acb0d 100644 --- a/config/config_json.go +++ b/config/config_json.go @@ -31,7 +31,6 @@ const ( STORDB_JSN = "stor_db" FilterSjsn = "filters" RALS_JSN = "rals" - SCHEDULER_JSN = "schedulers" CDRS_JSN = "cdrs" SessionSJson = "sessions" FreeSWITCHAgentJSN = "freeswitch_agent" @@ -71,7 +70,7 @@ const ( ) var ( - sortedCfgSections = []string{GENERAL_JSN, RPCConnsJsonName, DATADB_JSN, STORDB_JSN, LISTEN_JSN, TlsCfgJson, HTTP_JSN, SCHEDULER_JSN, + sortedCfgSections = []string{GENERAL_JSN, RPCConnsJsonName, DATADB_JSN, STORDB_JSN, LISTEN_JSN, TlsCfgJson, HTTP_JSN, CACHE_JSN, FilterSjsn, RALS_JSN, CDRS_JSN, ERsJson, SessionSJson, AsteriskAgentJSN, FreeSWITCHAgentJSN, KamailioAgentJSN, DA_JSN, RA_JSN, HttpAgentJson, DNSAgentJson, ATTRIBUTE_JSN, ChargerSCfgJson, RESOURCES_JSON, STATS_JSON, THRESHOLDS_JSON, RouteSJson, LoaderJson, MAILER_JSN, SURETAX_JSON, CgrLoaderCfgJson, CgrMigratorCfgJson, DispatcherSJson, @@ -185,18 +184,6 @@ func (jsnCfg CgrJsonCfg) RalsJsonCfg() (*RalsJsonCfg, error) { return cfg, nil } -func (jsnCfg CgrJsonCfg) SchedulerJsonCfg() (*SchedulerJsonCfg, error) { - rawCfg, hasKey := jsnCfg[SCHEDULER_JSN] - if !hasKey { - return nil, nil - } - cfg := new(SchedulerJsonCfg) - if err := json.Unmarshal(*rawCfg, cfg); err != nil { - return nil, err - } - return cfg, nil -} - func (jsnCfg CgrJsonCfg) CdrsJsonCfg() (*CdrsJsonCfg, error) { rawCfg, hasKey := jsnCfg[CDRS_JSN] if !hasKey { diff --git a/config/configsanity.go b/config/configsanity.go index 0809755c9..31d41f716 100644 --- a/config/configsanity.go +++ b/config/configsanity.go @@ -501,33 +501,6 @@ func (cfg *CGRConfig) checkConfigSanity() error { } } } - // Scheduler check connection with CDR Server - if cfg.schedulerCfg.Enabled { - for _, connID := range cfg.schedulerCfg.CDRsConns { - if strings.HasPrefix(connID, utils.MetaInternal) && !cfg.cdrsCfg.Enabled { - return fmt.Errorf("<%s> not enabled but requested by <%s> component", utils.CDRs, utils.SchedulerS) - } - if _, has := cfg.rpcConns[connID]; !has && !strings.HasPrefix(connID, utils.MetaInternal) { - return fmt.Errorf("<%s> connection with id: <%s> not defined", utils.SchedulerS, connID) - } - } - for _, connID := range cfg.schedulerCfg.ThreshSConns { - if strings.HasPrefix(connID, utils.MetaInternal) && !cfg.thresholdSCfg.Enabled { - return fmt.Errorf("<%s> not enabled but requested by <%s> component", utils.ThresholdS, utils.SchedulerS) - } - if _, has := cfg.rpcConns[connID]; !has && !strings.HasPrefix(connID, utils.MetaInternal) { - return fmt.Errorf("<%s> connection with id: <%s> not defined", utils.SchedulerS, connID) - } - } - for _, connID := range cfg.schedulerCfg.StatSConns { - if strings.HasPrefix(connID, utils.MetaInternal) && !cfg.statsCfg.Enabled { - return fmt.Errorf("<%s> not enabled but requested by <%s> component", utils.StatS, utils.SchedulerS) - } - if _, has := cfg.rpcConns[connID]; !has && !strings.HasPrefix(connID, utils.MetaInternal) { - return fmt.Errorf("<%s> connection with id: <%s> not defined", utils.SchedulerS, connID) - } - } - } // EventReader sanity checks if cfg.ersCfg.Enabled { for _, connID := range cfg.ersCfg.SessionSConns { @@ -678,8 +651,8 @@ func (cfg *CGRConfig) checkConfigSanity() error { return fmt.Errorf("<%s> connection with id: <%s> not defined", utils.APIerSv1, connID) } } - for _, connID := range cfg.apier.SchedulerConns { - if strings.HasPrefix(connID, utils.MetaInternal) && !cfg.schedulerCfg.Enabled { + for _, connID := range cfg.apier.ActionConns { + if strings.HasPrefix(connID, utils.MetaInternal) && !cfg.actionSCfg.Enabled { return fmt.Errorf("<%s> not enabled but requested by <%s> component", utils.SchedulerS, utils.APIerSv1) } if _, has := cfg.rpcConns[connID]; !has && !strings.HasPrefix(connID, utils.MetaInternal) { diff --git a/config/libconfig_json.go b/config/libconfig_json.go index 2b9910885..e751bb52e 100644 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -129,15 +129,6 @@ type RalsJsonCfg struct { Dynaprepaid_actionplans *[]string } -// Scheduler config section -type SchedulerJsonCfg struct { - Enabled *bool - Cdrs_conns *[]string - Thresholds_conns *[]string - Stats_conns *[]string - Filters *[]string -} - // Cdrs config section type CdrsJsonCfg struct { Enabled *bool diff --git a/config/schedulercfg.go b/config/schedulercfg.go deleted file mode 100644 index ee818b878..000000000 --- a/config/schedulercfg.go +++ /dev/null @@ -1,150 +0,0 @@ -/* -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 config - -import ( - "github.com/cgrates/cgrates/utils" -) - -// SchedulerCfg the condig section for scheduler -type SchedulerCfg struct { - Enabled bool - CDRsConns []string - ThreshSConns []string - StatSConns []string - Filters []string -} - -func (schdcfg *SchedulerCfg) loadFromJSONCfg(jsnCfg *SchedulerJsonCfg) error { - if jsnCfg == nil { - return nil - } - if jsnCfg.Enabled != nil { - schdcfg.Enabled = *jsnCfg.Enabled - } - if jsnCfg.Cdrs_conns != nil { - schdcfg.CDRsConns = make([]string, len(*jsnCfg.Cdrs_conns)) - for idx, conn := range *jsnCfg.Cdrs_conns { - // if we have the connection internal we change the name so we can have internal rpc for each subsystem - schdcfg.CDRsConns[idx] = conn - if conn == utils.MetaInternal { - schdcfg.CDRsConns[idx] = utils.ConcatenatedKey(utils.MetaInternal, utils.MetaCDRs) - } - } - } - if jsnCfg.Filters != nil { - schdcfg.Filters = make([]string, len(*jsnCfg.Filters)) - for i, fltr := range *jsnCfg.Filters { - schdcfg.Filters[i] = fltr - } - } - if jsnCfg.Thresholds_conns != nil { - schdcfg.ThreshSConns = make([]string, len(*jsnCfg.Thresholds_conns)) - for idx, connID := range *jsnCfg.Thresholds_conns { - // if we have the connection internal we change the name so we can have internal rpc for each subsystem - schdcfg.ThreshSConns[idx] = connID - if connID == utils.MetaInternal { - schdcfg.ThreshSConns[idx] = utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds) - } - } - } - if jsnCfg.Stats_conns != nil { - schdcfg.StatSConns = make([]string, len(*jsnCfg.Stats_conns)) - for idx, connID := range *jsnCfg.Stats_conns { - // if we have the connection internal we change the name so we can have internal rpc for each subsystem - schdcfg.StatSConns[idx] = connID - if connID == utils.MetaInternal { - schdcfg.StatSConns[idx] = utils.ConcatenatedKey(utils.MetaInternal, utils.MetaStats) - } - } - } - return nil -} - -// AsMapInterface returns the config as a map[string]interface{} -func (schdcfg *SchedulerCfg) AsMapInterface() (initialMP map[string]interface{}) { - initialMP = map[string]interface{}{ - utils.EnabledCfg: schdcfg.Enabled, - utils.FiltersCfg: schdcfg.Filters, - } - if schdcfg.CDRsConns != nil { - cdrsConns := make([]string, len(schdcfg.CDRsConns)) - for i, item := range schdcfg.CDRsConns { - cdrsConns[i] = item - if item == utils.ConcatenatedKey(utils.MetaInternal, utils.MetaCDRs) { - cdrsConns[i] = utils.MetaInternal - } - } - initialMP[utils.CDRsConnsCfg] = cdrsConns - } - if schdcfg.ThreshSConns != nil { - thrsConns := make([]string, len(schdcfg.ThreshSConns)) - for i, item := range schdcfg.ThreshSConns { - thrsConns[i] = item - if item == utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds) { - thrsConns[i] = utils.MetaInternal - } - } - initialMP[utils.ThreshSConnsCfg] = thrsConns - } - if schdcfg.StatSConns != nil { - stsConns := make([]string, len(schdcfg.StatSConns)) - for i, item := range schdcfg.StatSConns { - stsConns[i] = item - if item == utils.ConcatenatedKey(utils.MetaInternal, utils.MetaStats) { - stsConns[i] = utils.MetaInternal - } - } - initialMP[utils.StatSConnsCfg] = stsConns - } - return -} - -// Clone returns a deep copy of SchedulerCfg -func (schdcfg SchedulerCfg) Clone() (cln *SchedulerCfg) { - cln = &SchedulerCfg{ - Enabled: schdcfg.Enabled, - } - if schdcfg.CDRsConns != nil { - cln.CDRsConns = make([]string, len(schdcfg.CDRsConns)) - for i, con := range schdcfg.CDRsConns { - cln.CDRsConns[i] = con - } - } - if schdcfg.ThreshSConns != nil { - cln.ThreshSConns = make([]string, len(schdcfg.ThreshSConns)) - for i, con := range schdcfg.ThreshSConns { - cln.ThreshSConns[i] = con - } - } - if schdcfg.StatSConns != nil { - cln.StatSConns = make([]string, len(schdcfg.StatSConns)) - for i, con := range schdcfg.StatSConns { - cln.StatSConns[i] = con - } - } - if schdcfg.Filters != nil { - cln.Filters = make([]string, len(schdcfg.Filters)) - for i, con := range schdcfg.Filters { - cln.Filters[i] = con - } - } - - return -} diff --git a/config/schedulercfg_test.go b/config/schedulercfg_test.go deleted file mode 100644 index c94292e7d..000000000 --- a/config/schedulercfg_test.go +++ /dev/null @@ -1,118 +0,0 @@ -/* -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 config - -import ( - "reflect" - "testing" - - "github.com/cgrates/cgrates/utils" -) - -func TestSchedulerCfgloadFromJsonCfg(t *testing.T) { - cfgJSONS := &SchedulerJsonCfg{ - Enabled: utils.BoolPointer(true), - Cdrs_conns: &[]string{utils.MetaInternal, "*conn1"}, - Thresholds_conns: &[]string{utils.MetaInternal, "*conn1"}, - Stats_conns: &[]string{utils.MetaInternal, "*conn1"}, - Filters: &[]string{"randomFilter"}, - } - expected := &SchedulerCfg{ - Enabled: true, - CDRsConns: []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaCDRs), "*conn1"}, - ThreshSConns: []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds), "*conn1"}, - StatSConns: []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaStats), "*conn1"}, - Filters: []string{"randomFilter"}, - } - jsonCfg := NewDefaultCGRConfig() - if err = jsonCfg.schedulerCfg.loadFromJSONCfg(cfgJSONS); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(expected, jsonCfg.schedulerCfg) { - t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expected), utils.ToJSON(jsonCfg.schedulerCfg)) - } -} - -func TestSchedulerCfgAsMapInterface(t *testing.T) { - cfgJSONStr := `{ - "schedulers": {}, -}` - eMap := map[string]interface{}{ - utils.EnabledCfg: false, - utils.CDRsConnsCfg: []string{}, - utils.ThreshSConnsCfg: []string{}, - utils.StatSConnsCfg: []string{}, - utils.FiltersCfg: []string{}, - } - if cgrCfg, err := NewCGRConfigFromJSONStringWithDefaults(cfgJSONStr); err != nil { - t.Error(err) - } else if rcv := cgrCfg.schedulerCfg.AsMapInterface(); !reflect.DeepEqual(eMap, rcv) { - t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(eMap), utils.ToJSON(rcv)) - } - -} - -func TestSchedulerCfgAsMapInterface1(t *testing.T) { - cfgJSONStr := `{ - "schedulers": { - "enabled": true, - "cdrs_conns": ["*internal", "*conn1"], - "thresholds_conns": ["*internal", "*conn1"], - "stats_conns": ["*internal", "*conn1"], - "filters": ["randomFilter"], - }, -}` - eMap := map[string]interface{}{ - utils.EnabledCfg: true, - utils.CDRsConnsCfg: []string{utils.MetaInternal, "*conn1"}, - utils.ThreshSConnsCfg: []string{utils.MetaInternal, "*conn1"}, - utils.StatSConnsCfg: []string{utils.MetaInternal, "*conn1"}, - utils.FiltersCfg: []string{"randomFilter"}, - } - if cgrCfg, err := NewCGRConfigFromJSONStringWithDefaults(cfgJSONStr); err != nil { - t.Error(err) - } else if rcv := cgrCfg.schedulerCfg.AsMapInterface(); !reflect.DeepEqual(eMap, rcv) { - t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(eMap), utils.ToJSON(rcv)) - } - -} - -func TestSchedulerCfgClone(t *testing.T) { - ban := &SchedulerCfg{ - Enabled: true, - CDRsConns: []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaCDRs), "*conn1"}, - ThreshSConns: []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds), "*conn1"}, - StatSConns: []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaStats), "*conn1"}, - Filters: []string{"randomFilter"}, - } - rcv := ban.Clone() - if !reflect.DeepEqual(ban, rcv) { - t.Errorf("Expected: %+v\nReceived: %+v", utils.ToJSON(ban), utils.ToJSON(rcv)) - } - if rcv.CDRsConns[1] = ""; ban.CDRsConns[1] != "*conn1" { - t.Errorf("Expected clone to not modify the cloned") - } - if rcv.ThreshSConns[1] = ""; ban.ThreshSConns[1] != "*conn1" { - t.Errorf("Expected clone to not modify the cloned") - } - if rcv.StatSConns[1] = ""; ban.StatSConns[1] != "*conn1" { - t.Errorf("Expected clone to not modify the cloned") - } - if rcv.Filters[0] = ""; ban.Filters[0] != "randomFilter" { - t.Errorf("Expected clone to not modify the cloned") - } -} diff --git a/console/scheduler_execute.go b/console/scheduler_execute.go deleted file mode 100644 index 042882215..000000000 --- a/console/scheduler_execute.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments -Copyright (C) ITsysCOM GmbH - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package console - -import ( - "github.com/cgrates/cgrates/utils" -) - -func init() { - c := &CmdExecuteScheduledActions{ - name: "scheduler_execute", - rpcMethod: utils.SchedulerSv1ExecuteActions, - rpcParams: &utils.AttrsExecuteActions{}, - } - commands[c.Name()] = c - c.CommandExecuter = &CommandExecuter{c} -} - -// Commander implementation -type CmdExecuteScheduledActions struct { - name string - rpcMethod string - rpcParams *utils.AttrsExecuteActions - *CommandExecuter -} - -func (self *CmdExecuteScheduledActions) Name() string { - return self.name -} - -func (self *CmdExecuteScheduledActions) RpcMethod() string { - return self.rpcMethod -} - -func (self *CmdExecuteScheduledActions) RpcParams(reset bool) interface{} { - if reset || self.rpcParams == nil { - self.rpcParams = &utils.AttrsExecuteActions{} - } - return self.rpcParams -} - -func (self *CmdExecuteScheduledActions) PostprocessRpcParams() error { - return nil -} - -func (self *CmdExecuteScheduledActions) RpcResult() interface{} { - var s string - return &s -} diff --git a/console/scheduler_execute_test.go b/console/scheduler_execute_test.go deleted file mode 100644 index e69c4af5f..000000000 --- a/console/scheduler_execute_test.go +++ /dev/null @@ -1,54 +0,0 @@ -/* -Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments -Copyright (C) ITsysCOM GmbH - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package console - -import ( - "reflect" - "strings" - "testing" - - v1 "github.com/cgrates/cgrates/apier/v1" - - "github.com/cgrates/cgrates/utils" -) - -func TestCmdSchedulerExecute(t *testing.T) { - // commands map is initiated in init function - command := commands["scheduler_execute"] - // verify if ApierSv1 object has method on it - m, ok := reflect.TypeOf(new(v1.SchedulerSv1)).MethodByName(strings.Split(command.RpcMethod(), utils.NestingSep)[1]) - if !ok { - t.Fatal("method not found") - } - if m.Type.NumIn() != 3 { // ApierSv1 is consider and we expect 3 inputs - t.Fatalf("invalid number of input parameters ") - } - // verify the type of input parameter - if ok := m.Type.In(1).AssignableTo(reflect.TypeOf(command.RpcParams(true))); !ok { - t.Fatalf("cannot assign input parameter") - } - // verify the type of output parameter - if ok := m.Type.In(2).AssignableTo(reflect.TypeOf(command.RpcResult())); !ok { - t.Fatalf("cannot assign output parameter") - } - // for coverage purpose - if err := command.PostprocessRpcParams(); err != nil { - t.Fatal(err) - } -} diff --git a/console/scheduler_queue.go b/console/scheduler_queue.go deleted file mode 100644 index 3de9ad07f..000000000 --- a/console/scheduler_queue.go +++ /dev/null @@ -1,66 +0,0 @@ -/* -Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments -Copyright (C) ITsysCOM GmbH - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package console - -import ( - "github.com/cgrates/cgrates/scheduler" - "github.com/cgrates/cgrates/utils" -) - -func init() { - c := &CmdGetScheduledActions{ - name: "scheduler_queue", - rpcMethod: utils.APIerSv1GetScheduledActions, - rpcParams: &scheduler.ArgsGetScheduledActions{}, - } - commands[c.Name()] = c - c.CommandExecuter = &CommandExecuter{c} -} - -// Commander implementation -type CmdGetScheduledActions struct { - name string - rpcMethod string - rpcParams *scheduler.ArgsGetScheduledActions - *CommandExecuter -} - -func (self *CmdGetScheduledActions) Name() string { - return self.name -} - -func (self *CmdGetScheduledActions) RpcMethod() string { - return self.rpcMethod -} - -func (self *CmdGetScheduledActions) RpcParams(reset bool) interface{} { - if reset || self.rpcParams == nil { - self.rpcParams = &scheduler.ArgsGetScheduledActions{} - } - return self.rpcParams -} - -func (self *CmdGetScheduledActions) PostprocessRpcParams() error { - return nil -} - -func (self *CmdGetScheduledActions) RpcResult() interface{} { - s := make([]*scheduler.ScheduledAction, 0) - return &s -} diff --git a/console/scheduler_queue_test.go b/console/scheduler_queue_test.go deleted file mode 100644 index 5a4c38de6..000000000 --- a/console/scheduler_queue_test.go +++ /dev/null @@ -1,54 +0,0 @@ -/* -Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments -Copyright (C) ITsysCOM GmbH - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package console - -import ( - "reflect" - "strings" - "testing" - - v1 "github.com/cgrates/cgrates/apier/v1" - - "github.com/cgrates/cgrates/utils" -) - -func TestCmdSchedulerQueue(t *testing.T) { - // commands map is initiated in init function - command := commands["scheduler_queue"] - // verify if ApierSv1 object has method on it - m, ok := reflect.TypeOf(new(v1.APIerSv1)).MethodByName(strings.Split(command.RpcMethod(), utils.NestingSep)[1]) - if !ok { - t.Fatal("method not found") - } - if m.Type.NumIn() != 3 { // ApierSv1 is consider and we expect 3 inputs - t.Fatalf("invalid number of input parameters ") - } - // verify the type of input parameter - if ok := m.Type.In(1).AssignableTo(reflect.TypeOf(command.RpcParams(true))); !ok { - t.Fatalf("cannot assign input parameter") - } - // verify the type of output parameter - if ok := m.Type.In(2).AssignableTo(reflect.TypeOf(command.RpcResult())); !ok { - t.Fatalf("cannot assign output parameter") - } - // for coverage purpose - if err := command.PostprocessRpcParams(); err != nil { - t.Fatal(err) - } -} diff --git a/console/scheduler_reload.go b/console/scheduler_reload.go deleted file mode 100644 index 0aab73882..000000000 --- a/console/scheduler_reload.go +++ /dev/null @@ -1,62 +0,0 @@ -/* -Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments -Copyright (C) ITsysCOM GmbH - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package console - -import "github.com/cgrates/cgrates/utils" - -func init() { - c := &CmdReloadScheduler{ - name: "scheduler_reload", - rpcMethod: utils.SchedulerSv1Reload, - } - commands[c.Name()] = c - c.CommandExecuter = &CommandExecuter{c} -} - -// Commander implementation -type CmdReloadScheduler struct { - name string - rpcMethod string - rpcParams *utils.CGREvent - *CommandExecuter -} - -func (self *CmdReloadScheduler) Name() string { - return self.name -} - -func (self *CmdReloadScheduler) RpcMethod() string { - return self.rpcMethod -} - -func (self *CmdReloadScheduler) RpcParams(reset bool) interface{} { - if reset || self.rpcParams == nil { - self.rpcParams = &utils.CGREvent{} - } - return self.rpcParams -} - -func (self *CmdReloadScheduler) PostprocessRpcParams() error { - return nil -} - -func (self *CmdReloadScheduler) RpcResult() interface{} { - var s string - return &s -} diff --git a/console/scheduler_reload_test.go b/console/scheduler_reload_test.go deleted file mode 100644 index b551ca15c..000000000 --- a/console/scheduler_reload_test.go +++ /dev/null @@ -1,54 +0,0 @@ -/* -Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments -Copyright (C) ITsysCOM GmbH - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package console - -import ( - "reflect" - "strings" - "testing" - - v1 "github.com/cgrates/cgrates/apier/v1" - - "github.com/cgrates/cgrates/utils" -) - -func TestCmdSchedulerReload(t *testing.T) { - // commands map is initiated in init function - command := commands["scheduler_reload"] - // verify if ApierSv1 object has method on it - m, ok := reflect.TypeOf(new(v1.SchedulerSv1)).MethodByName(strings.Split(command.RpcMethod(), utils.NestingSep)[1]) - if !ok { - t.Fatal("method not found") - } - if m.Type.NumIn() != 3 { // ApierSv1 is consider and we expect 3 inputs - t.Fatalf("invalid number of input parameters ") - } - // verify the type of input parameter - if ok := m.Type.In(1).AssignableTo(reflect.TypeOf(command.RpcParams(true))); !ok { - t.Fatalf("cannot assign input parameter") - } - // verify the type of output parameter - if ok := m.Type.In(2).AssignableTo(reflect.TypeOf(command.RpcResult())); !ok { - t.Fatalf("cannot assign output parameter") - } - // for coverage purpose - if err := command.PostprocessRpcParams(); err != nil { - t.Fatal(err) - } -} diff --git a/engine/account.go b/engine/account.go deleted file mode 100644 index bb7c2c665..000000000 --- a/engine/account.go +++ /dev/null @@ -1,1268 +0,0 @@ -/* -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 ( - "encoding/json" - "errors" - "fmt" - "net" - "strings" - "time" - - "github.com/cgrates/cgrates/config" - "github.com/cgrates/cgrates/guardian" - "github.com/cgrates/cgrates/structmatcher" - "github.com/cgrates/cgrates/utils" -) - -// Account structure containing information about user's credit (minutes, cents, sms...).' -// This can represent a user or a shared group. -type Account struct { - ID string - BalanceMap map[string]Balances - UnitCounters UnitCounters - ActionTriggers ActionTriggers - AllowNegative bool - Disabled bool - UpdateTime time.Time - executingTriggers bool -} - -type AccountWithAPIOpts struct { - *Account - APIOpts map[string]interface{} -} - -// User's available minutes for the specified destination -func (acc *Account) getCreditForPrefix(cd *CallDescriptor) (duration time.Duration, credit float64, balances Balances) { - creditBalances := acc.getBalancesForPrefix(cd.Destination, cd.Category, utils.MetaMonetary, "", cd.TimeStart) - - unitBalances := acc.getBalancesForPrefix(cd.Destination, cd.Category, cd.ToR, "", cd.TimeStart) - // gather all balances from shared groups - var extendedCreditBalances Balances - for _, cb := range creditBalances { - if len(cb.SharedGroups) > 0 { - for sg := range cb.SharedGroups { - if sharedGroup, _ := dm.GetSharedGroup(sg, false, utils.NonTransactional); sharedGroup != nil { - sgb := sharedGroup.GetBalances(cd.Destination, cd.Category, utils.MetaMonetary, acc, cd.TimeStart) - sgb = sharedGroup.SortBalancesByStrategy(cb, sgb) - extendedCreditBalances = append(extendedCreditBalances, sgb...) - } - } - } else { - extendedCreditBalances = append(extendedCreditBalances, cb) - } - } - var extendedMinuteBalances Balances - for _, mb := range unitBalances { - if len(mb.SharedGroups) > 0 { - for sg := range mb.SharedGroups { - if sharedGroup, _ := dm.GetSharedGroup(sg, false, utils.NonTransactional); sharedGroup != nil { - sgb := sharedGroup.GetBalances(cd.Destination, cd.Category, cd.ToR, acc, cd.TimeStart) - sgb = sharedGroup.SortBalancesByStrategy(mb, sgb) - extendedMinuteBalances = append(extendedMinuteBalances, sgb...) - } - } - } else { - extendedMinuteBalances = append(extendedMinuteBalances, mb) - } - } - credit = extendedCreditBalances.GetTotalValue() - balances = extendedMinuteBalances - for _, b := range balances { - d, c := b.GetMinutesForCredit(cd, credit) - credit = c - duration += d - } - return -} - -// sets all the fields of the balance -func (acc *Account) setBalanceAction(a *Action) error { - if a == nil { - return errors.New("nil action") - } - if acc.BalanceMap == nil { - acc.BalanceMap = make(map[string]Balances) - } - var balance *Balance - var found bool - var previousSharedGroups utils.StringMap // kept for comparison - if a.Balance.Uuid != nil && *a.Balance.Uuid != "" { // balance uuid match - for balanceType := range acc.BalanceMap { - for _, b := range acc.BalanceMap[balanceType] { - if b.Uuid == *a.Balance.Uuid && !b.IsExpiredAt(time.Now()) { - previousSharedGroups = b.SharedGroups - balance = b - found = true - break // only set one balance - } - } - if found { - break - } - } - if !found { - return fmt.Errorf("cannot find balance with uuid: <%s>", *a.Balance.Uuid) - } - } else { // balance id match - for balanceType := range acc.BalanceMap { - for _, b := range acc.BalanceMap[balanceType] { - if a.Balance.ID != nil && b.ID == *a.Balance.ID && !b.IsExpiredAt(time.Now()) { - previousSharedGroups = b.SharedGroups - balance = b - found = true - break // only set one balance - } - } - if found { - break - } - } - // if it is not found then we create it - if !found { - if a.Balance.Type == nil { // cannot create the entry in the balance map without this info - return errors.New("missing balance type") - } - balance = &Balance{} - balance.Uuid = utils.GenUUID() // alway overwrite the uuid for consistency - acc.BalanceMap[*a.Balance.Type] = append(acc.BalanceMap[*a.Balance.Type], balance) - } - } - if a.Balance.ID != nil && *a.Balance.ID == utils.MetaDefault { // treat it separately since modifyBalance sets expiry and others parameters, not specific for *default - if a.Balance.Value != nil { - balance.ID = *a.Balance.ID - balance.Value = a.Balance.GetValue() - balance.SetDirty() // Mark the balance as dirty since we have modified and it should be checked by action triggers - } - } else { - a.Balance.ModifyBalance(balance) - } - // modify if necessary the shared groups here - if !found || !previousSharedGroups.Equal(balance.SharedGroups) { - _, err := guardian.Guardian.Guard(func() (interface{}, error) { - i := 0 - for sgID := range balance.SharedGroups { - // add shared group member - sg, err := dm.GetSharedGroup(sgID, false, utils.NonTransactional) - if err != nil || sg == nil { - //than is problem - utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgID)) - } else { - if _, found := sg.MemberIds[acc.ID]; !found { - // add member and save - if sg.MemberIds == nil { - sg.MemberIds = make(utils.StringMap) - } - sg.MemberIds[acc.ID] = true - dm.SetSharedGroup(sg, utils.NonTransactional) - } - } - i++ - } - return 0, nil - }, config.CgrConfig().GeneralCfg().LockingTimeout, balance.SharedGroups.Slice()...) - if err != nil { - return err - } - } - acc.InitCounters() - acc.ExecuteActionTriggers(nil) - return nil -} - -// Debits some amount of user's specified balance adding the balance if it does not exists. -// Returns the remaining credit in user's balance. -func (acc *Account) debitBalanceAction(a *Action, reset, resetIfNegative bool) error { - if a == nil { - return errors.New("nil action") - } - bClone := a.Balance.CreateBalance() - //log.Print("Bclone: ", utils.ToJSON(a.Balance)) - if bClone == nil { - return errors.New("nil balance in action") - } - if acc.BalanceMap == nil { - acc.BalanceMap = make(map[string]Balances) - } - found := false - balanceType := a.Balance.GetType() - for _, b := range acc.BalanceMap[balanceType] { - if b.IsExpiredAt(time.Now()) { - continue // just to be safe (cleaned expired balances above) - } - b.account = acc - if b.MatchFilter(a.Balance, false, false) { - if reset || (resetIfNegative && b.Value < 0) { - b.SetValue(0) - } - b.SubstractValue(bClone.GetValue()) - b.dirty = true - found = true - a.balanceValue = b.GetValue() - } - } - // if it is not found then we add it to the list - if !found { - // check if the Id is *default (user trying to create the default balance) - // use only it's value value - if bClone.ID == utils.MetaDefault { - bClone = &Balance{ - ID: utils.MetaDefault, - Value: -bClone.GetValue(), - } - } else { - if bClone.GetValue() != 0 { - bClone.SetValue(-bClone.GetValue()) - } - } - bClone.dirty = true // Mark the balance as dirty since we have modified and it should be checked by action triggers - a.balanceValue = bClone.GetValue() - bClone.Uuid = utils.GenUUID() // alway overwrite the uuid for consistency - // load ValueFactor if defined in extra parametrs - if a.ExtraParameters != "" { - vf := ValueFactor{} - err := json.Unmarshal([]byte(a.ExtraParameters), &vf) - if err == nil { - bClone.Factor = vf - } else { - utils.Logger.Warning(fmt.Sprintf("Could load value factor from actions: extra parametrs: %s", a.ExtraParameters)) - } - } - acc.BalanceMap[balanceType] = append(acc.BalanceMap[balanceType], bClone) - _, err := guardian.Guardian.Guard(func() (interface{}, error) { - sgs := make([]string, len(bClone.SharedGroups)) - i := 0 - for sgID := range bClone.SharedGroups { - // add shared group member - sg, err := dm.GetSharedGroup(sgID, false, utils.NonTransactional) - if err != nil || sg == nil { - //than is problem - utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgID)) - } else { - if _, found := sg.MemberIds[acc.ID]; !found { - // add member and save - if sg.MemberIds == nil { - sg.MemberIds = make(utils.StringMap) - } - sg.MemberIds[acc.ID] = true - dm.SetSharedGroup(sg, utils.NonTransactional) - } - } - i++ - } - dm.CacheDataFromDB(utils.SharedGroupPrefix, sgs, true) - return 0, nil - }, config.CgrConfig().GeneralCfg().LockingTimeout, bClone.SharedGroups.Slice()...) - if err != nil { - return err - } - } - acc.InitCounters() - acc.ExecuteActionTriggers(nil) - return nil -} - -func (acc *Account) getBalancesForPrefix(prefix, category, tor, - sharedGroup string, aTime time.Time) Balances { - var balances Balances - balances = append(balances, acc.BalanceMap[tor]...) - if tor != utils.MetaMonetary && tor != utils.MetaGeneric { - balances = append(balances, acc.BalanceMap[utils.MetaGeneric]...) - } - - var usefulBalances Balances - for _, b := range balances { - if b.Disabled { - continue - } - if b.IsExpiredAt(aTime) || (len(b.SharedGroups) == 0 && b.GetValue() <= 0 && !b.Blocker) { - continue - } - if sharedGroup != "" && !b.SharedGroups[sharedGroup] { - continue - } - if !b.MatchCategory(category) { - continue - } - b.account = acc - - if len(b.DestinationIDs) > 0 && !b.DestinationIDs[utils.MetaAny] { - for _, p := range utils.SplitPrefix(prefix, MIN_PREFIX_MATCH) { - if destIDs, err := dm.GetReverseDestination(p, true, true, utils.NonTransactional); err == nil { - foundResult := false - allInclude := true // whether it is excluded or included - for _, dID := range destIDs { - inclDest, found := b.DestinationIDs[dID] - if found { - foundResult = true - allInclude = allInclude && inclDest - } - } - // check wheter all destination ids in the balance were exclusions - allExclude := true - for _, inclDest := range b.DestinationIDs { - if inclDest { - allExclude = false - break - } - } - if foundResult || allExclude { - if allInclude { - b.precision = len(p) - usefulBalances = append(usefulBalances, b) - } else { - b.precision = 1 // fake to exit the outer loop - } - } - } - if b.precision > 0 { - break - } - } - } else { - usefulBalances = append(usefulBalances, b) - } - } - - // resort by precision - usefulBalances.Sort() - // clear precision - for _, b := range usefulBalances { - b.precision = 0 - } - return usefulBalances -} - -// like getBalancesForPrefix but expanding shared balances -func (acc *Account) getAlldBalancesForPrefix(destination, category, - balanceType string, aTime time.Time) (bc Balances) { - balances := acc.getBalancesForPrefix(destination, category, balanceType, "", aTime) - for _, b := range balances { - if len(b.SharedGroups) > 0 { - for sgID := range b.SharedGroups { - sharedGroup, err := dm.GetSharedGroup(sgID, false, utils.NonTransactional) - if err != nil || sharedGroup == nil { - utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgID)) - continue - } - sharedBalances := sharedGroup.GetBalances(destination, category, balanceType, acc, aTime) - sharedBalances = sharedGroup.SortBalancesByStrategy(b, sharedBalances) - bc = append(bc, sharedBalances...) - } - } else { - bc = append(bc, b) - } - } - return -} - -func (acc *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun bool, goNegative bool) (cc *CallCost, err error) { - usefulUnitBalances := acc.getAlldBalancesForPrefix(cd.Destination, cd.Category, cd.ToR, cd.TimeStart) - usefulMoneyBalances := acc.getAlldBalancesForPrefix(cd.Destination, cd.Category, utils.MetaMonetary, cd.TimeStart) - var leftCC *CallCost - cc = cd.CreateCallCost() - var hadBalanceSubj bool - generalBalanceChecker := true - for generalBalanceChecker { - generalBalanceChecker = false - - // debit minutes - unitBalanceChecker := true - for unitBalanceChecker { - // try every balance multiple times in case one becomes active or ratig changes - unitBalanceChecker = false - for _, balance := range usefulUnitBalances { - partCC, debitErr := balance.debitUnits(cd, balance.account, - usefulMoneyBalances, count, dryRun, len(cc.Timespans) == 0) - if debitErr != nil { - return nil, debitErr - } - if balance.RatingSubject != "" && - !strings.HasPrefix(balance.RatingSubject, utils.MetaRatingSubjectPrefix) { - hadBalanceSubj = true - } - if partCC != nil { - cc.Timespans = append(cc.Timespans, partCC.Timespans...) - cc.negativeConnectFee = partCC.negativeConnectFee - cd.TimeStart = cc.GetEndTime() - // check if the calldescriptor is covered - if cd.GetDuration() <= 0 { - goto COMMIT - } - unitBalanceChecker = true - generalBalanceChecker = true - // check for max cost disconnect - if dryRun && partCC.maxCostDisconect { - // only return if we are in dry run (max call duration) - return - } - } - // check for blocker - if dryRun && balance.Blocker { - return // don't go to next balances - } - } - } - // debit money - moneyBalanceChecker := true - for moneyBalanceChecker { - // try every balance multiple times in case one becomes active or ratig changes - moneyBalanceChecker = false - for _, balance := range usefulMoneyBalances { - partCC, debitErr := balance.debitMoney(cd, balance.account, - usefulMoneyBalances, count, dryRun, len(cc.Timespans) == 0) - if debitErr != nil { - return nil, debitErr - } - if partCC != nil { - cc.Timespans = append(cc.Timespans, partCC.Timespans...) - cc.negativeConnectFee = partCC.negativeConnectFee - - cd.TimeStart = cc.GetEndTime() - // check if the calldescriptor is covered - if cd.GetDuration() <= 0 { - goto COMMIT - } - moneyBalanceChecker = true - generalBalanceChecker = true - if dryRun && partCC.maxCostDisconect { - // only return if we are in dry run (max call duration) - return - } - } - // check for blocker - if dryRun && balance.Blocker { - return // don't go to next balances - } - } - } - } - if hadBalanceSubj { - cd.RatingInfos = nil - } - leftCC, err = cd.getCost() - if err != nil { - utils.Logger.Err(fmt.Sprintf("Error getting new cost for balance subject: %v", err)) - } - if leftCC.Cost == 0 && len(leftCC.Timespans) > 0 { - // put AccountID ubformation in increments - for _, ts := range leftCC.Timespans { - for _, inc := range ts.Increments { - if inc.BalanceInfo == nil { - inc.BalanceInfo = &DebitInfo{} - } - inc.BalanceInfo.AccountID = acc.ID - } - } - cc.Timespans = append(cc.Timespans, leftCC.Timespans...) - } - - if leftCC.Cost > 0 && goNegative { - initialLength := len(cc.Timespans) - cc.Timespans = append(cc.Timespans, leftCC.Timespans...) - - var debitedConnectFeeBalance Balance - var ok bool - - if initialLength == 0 { - // this is the first add, debit the connect fee - ok, debitedConnectFeeBalance = acc.DebitConnectionFee(cc, usefulMoneyBalances, count, true) - } - // get the default money balance - // and go negative on it with the amount still unpaid - if len(leftCC.Timespans) > 0 && leftCC.Cost > 0 && !acc.AllowNegative && !dryRun { - utils.Logger.Warning(fmt.Sprintf(" Going negative on account %s with AllowNegative: false", cd.GetAccountKey())) - } - leftCC.Timespans.Decompress() - for tsIndex, ts := range leftCC.Timespans { - if ts.Increments == nil { - ts.createIncrementsSlice() - } - - if tsIndex == 0 && ts.RateInterval.Rating.ConnectFee > 0 && cc.deductConnectFee && ok { - - inc := &Increment{ - Duration: 0, - Cost: ts.RateInterval.Rating.ConnectFee, - BalanceInfo: &DebitInfo{ - Monetary: &MonetaryInfo{ - UUID: debitedConnectFeeBalance.Uuid, - ID: debitedConnectFeeBalance.ID, - Value: debitedConnectFeeBalance.Value, - }, - AccountID: acc.ID, - }, - } - - incs := []*Increment{inc} - ts.Increments = append(incs, ts.Increments...) - } - - for incIndex, increment := range ts.Increments { - // connect fee was processed and skip it - if tsIndex == 0 && incIndex == 0 && ts.RateInterval.Rating.ConnectFee > 0 && cc.deductConnectFee && ok { - continue - } - cost := increment.Cost - defaultBalance := acc.GetDefaultMoneyBalance() - defaultBalance.SubstractValue(cost) - - increment.BalanceInfo.Monetary = &MonetaryInfo{ - UUID: defaultBalance.Uuid, - ID: defaultBalance.ID, - Value: defaultBalance.Value, - } - increment.BalanceInfo.AccountID = acc.ID - increment.paid = true - if count { - acc.countUnits( - cost, - utils.MetaMonetary, - leftCC, - &Balance{ - Value: cost, - DestinationIDs: utils.NewStringMap(leftCC.Destination), - }) - } - } - } - - // in case of going to negative we send the default balance to thresholdS to be processed - if len(config.CgrConfig().RalsCfg().ThresholdSConns) != 0 { - defaultBalance := acc.GetDefaultMoneyBalance() - acntTnt := utils.NewTenantID(acc.ID) - thEv := &ThresholdsArgsProcessEvent{ - CGREvent: &utils.CGREvent{ - Tenant: acntTnt.Tenant, - ID: utils.GenUUID(), - Event: map[string]interface{}{ - utils.EventType: utils.BalanceUpdate, - utils.EventSource: utils.AccountService, - utils.AccountField: acntTnt.ID, - utils.BalanceID: defaultBalance.ID, - utils.Units: defaultBalance.Value, - }, - APIOpts: map[string]interface{}{ - utils.MetaEventType: utils.BalanceUpdate, - }, - }, - } - var tIDs []string - if err := connMgr.Call(config.CgrConfig().RalsCfg().ThresholdSConns, nil, - utils.ThresholdSv1ProcessEvent, thEv, &tIDs); err != nil && - err.Error() != utils.ErrNotFound.Error() { - utils.Logger.Warning( - fmt.Sprintf(" error: <%s> processing balance event <%+v> with ThresholdS.", - err.Error(), utils.ToJSON(thEv))) - } - } - } - -COMMIT: - if !dryRun { - // save darty shared balances - usefulMoneyBalances.SaveDirtyBalances(acc) - usefulUnitBalances.SaveDirtyBalances(acc) - } - //log.Printf("Final CC: %+v", cc) - return -} - -// GetDefaultMoneyBalance returns the defaultmoney balance -func (acc *Account) GetDefaultMoneyBalance() *Balance { - for _, balance := range acc.BalanceMap[utils.MetaMonetary] { - if balance.IsDefault() { - return balance - } - } - // create default balance - defaultBalance := &Balance{ - Uuid: utils.GenUUID(), - ID: utils.MetaDefault, - } // minimum weight - if acc.BalanceMap == nil { - acc.BalanceMap = make(map[string]Balances) - } - acc.BalanceMap[utils.MetaMonetary] = append(acc.BalanceMap[utils.MetaMonetary], defaultBalance) - return defaultBalance -} - -// ExecuteActionTriggers scans the action triggers and execute the actions for which trigger is met -func (acc *Account) ExecuteActionTriggers(a *Action) { - if acc.executingTriggers { - return - } - acc.executingTriggers = true - defer func() { - acc.executingTriggers = false - }() - - acc.ActionTriggers.Sort() - for _, at := range acc.ActionTriggers { - // check is effective - if at.IsExpired(time.Now()) || !at.IsActive(time.Now()) { - continue - } - - // sanity check - if !strings.Contains(at.ThresholdType, "counter") && !strings.Contains(at.ThresholdType, "balance") { - continue - } - if at.Executed { - // trigger is marked as executed, so skipp it until - // the next reset (see RESET_TRIGGERS action type) - continue - } - if !at.Match(a) { - continue - } - if strings.Contains(at.ThresholdType, "counter") { - if (at.Balance.ID == nil || *at.Balance.ID != "") && at.UniqueID != "" { - at.Balance.ID = utils.StringPointer(at.UniqueID) - } - for _, counters := range acc.UnitCounters { - for _, uc := range counters { - if strings.Contains(at.ThresholdType, uc.CounterType[1:]) { - for _, c := range uc.Counters { - //log.Print("C: ", utils.ToJSON(c)) - if strings.HasPrefix(at.ThresholdType, "*max") { - if c.Filter.Equal(at.Balance) && c.Value >= at.ThresholdValue { - //log.Print("HERE") - at.Execute(acc) - } - } else { //MIN - if c.Filter.Equal(at.Balance) && c.Value <= at.ThresholdValue { - at.Execute(acc) - } - } - } - } - } - } - } else { // BALANCE - for _, b := range acc.BalanceMap[at.Balance.GetType()] { - if !b.dirty && at.ThresholdType != utils.TriggerBalanceExpired { // do not check clean balances - continue - } - switch at.ThresholdType { - case utils.TriggerMaxBalance: - if b.MatchActionTrigger(at) && b.GetValue() >= at.ThresholdValue { - at.Execute(acc) - } - case utils.TriggerMinBalance: - if b.MatchActionTrigger(at) && b.GetValue() <= at.ThresholdValue { - at.Execute(acc) - } - case utils.TriggerBalanceExpired: - if b.MatchActionTrigger(at) && b.IsExpiredAt(time.Now()) { - at.Execute(acc) - } - } - } - } - } - acc.CleanExpiredStuff() -} - -// ResetActionTriggers marks all action trigers as ready for execution -// If the action is not nil it acts like a filter -func (acc *Account) ResetActionTriggers(a *Action) { - for _, at := range acc.ActionTriggers { - if !at.Match(a) { - continue - } - at.Executed = false - } - acc.ExecuteActionTriggers(a) -} - -// SetRecurrent sets/unsets recurrent flag for action triggers -func (acc *Account) SetRecurrent(a *Action, recurrent bool) { - for _, at := range acc.ActionTriggers { - if !at.Match(a) { - continue - } - at.Recurrent = recurrent - } -} - -// Increments the counter for the type -func (acc *Account) countUnits(amount float64, kind string, cc *CallCost, b *Balance) { - acc.UnitCounters.addUnits(amount, kind, cc, b) - acc.ExecuteActionTriggers(nil) -} - -// InitCounters creates counters for all triggered actions -func (acc *Account) InitCounters() { - oldUcs := acc.UnitCounters - acc.UnitCounters = make(UnitCounters) - ucTempMap := make(map[string]*UnitCounter) - for _, at := range acc.ActionTriggers { - //log.Print("AT: ", utils.ToJSON(at)) - if !strings.Contains(at.ThresholdType, "counter") { - continue - } - ct := utils.MetaCounterEvent //default - if strings.Contains(at.ThresholdType, "balance") { - ct = utils.MetaBalance - } - uc, exists := ucTempMap[at.Balance.GetType()+ct] - //log.Print("CT: ", at.Balance.GetType()+ct) - if !exists { - uc = &UnitCounter{ - CounterType: ct, - } - ucTempMap[at.Balance.GetType()+ct] = uc - uc.Counters = make(CounterFilters, 0) - acc.UnitCounters[at.Balance.GetType()] = append(acc.UnitCounters[at.Balance.GetType()], uc) - } - - c := &CounterFilter{Filter: at.Balance.Clone()} - if (c.Filter.ID == nil || *c.Filter.ID == "") && at.UniqueID != "" { - c.Filter.ID = utils.StringPointer(at.UniqueID) - } - //log.Print("C: ", utils.ToJSON(c)) - if !uc.Counters.HasCounter(c) { - uc.Counters = append(uc.Counters, c) - } - } - // copy old counter values - for key, counters := range acc.UnitCounters { - oldCounters, found := oldUcs[key] - if !found { - continue - } - for _, uc := range counters { - for _, oldUc := range oldCounters { - if uc.CopyCounterValues(oldUc) { - break - } - } - } - } - if len(acc.UnitCounters) == 0 { - acc.UnitCounters = nil // leave it nil if empty - } -} - -// CleanExpiredStuff removed expired balances and actiontriggers -func (acc *Account) CleanExpiredStuff() { - if config.CgrConfig().RalsCfg().RemoveExpired { - for key, bm := range acc.BalanceMap { - for i := 0; i < len(bm); i++ { - if bm[i].IsExpiredAt(time.Now()) { - // delete it - bm = append(bm[:i], bm[i+1:]...) - } - } - acc.BalanceMap[key] = bm - } - } - - for i := 0; i < len(acc.ActionTriggers); i++ { - if acc.ActionTriggers[i].IsExpired(time.Now()) { - acc.ActionTriggers = append(acc.ActionTriggers[:i], acc.ActionTriggers[i+1:]...) - } - } -} - -func (acc *Account) allBalancesExpired() bool { - for _, bm := range acc.BalanceMap { - for i := 0; i < len(bm); i++ { - if !bm[i].IsExpiredAt(time.Now()) { - return false - } - } - } - return true -} - -// GetSharedGroups returns the shared groups that this user balance belnongs to -func (acc *Account) GetSharedGroups() (groups []string) { - for _, balanceChain := range acc.BalanceMap { - for _, b := range balanceChain { - for sg := range b.SharedGroups { - groups = append(groups, sg) - } - } - } - return -} - -// GetUniqueSharedGroupMembers returns the acounts from the group -func (acc *Account) GetUniqueSharedGroupMembers(cd *CallDescriptor) (utils.StringMap, error) { // ToDo: make sure we return accountIDs - var balances []*Balance - balances = append(balances, acc.getBalancesForPrefix(cd.Destination, cd.Category, utils.MetaMonetary, "", cd.TimeStart)...) - balances = append(balances, acc.getBalancesForPrefix(cd.Destination, cd.Category, cd.ToR, "", cd.TimeStart)...) - // gather all shared group ids - var sharedGroupIds []string - for _, b := range balances { - for sg := range b.SharedGroups { - sharedGroupIds = append(sharedGroupIds, sg) - } - } - memberIds := make(utils.StringMap) - for _, sgID := range sharedGroupIds { - sharedGroup, err := dm.GetSharedGroup(sgID, false, utils.NonTransactional) - if err != nil { - utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgID)) - return nil, err - } - for memberID := range sharedGroup.MemberIds { - memberIds[memberID] = true - } - } - return memberIds, nil -} - -// Clone creates a copy of the account -func (acc *Account) Clone() *Account { - newAcc := &Account{ - ID: acc.ID, - UnitCounters: acc.UnitCounters.Clone(), - AllowNegative: acc.AllowNegative, - Disabled: acc.Disabled, - } - if acc.BalanceMap != nil { - newAcc.BalanceMap = make(map[string]Balances, len(acc.BalanceMap)) - for key, balanceChain := range acc.BalanceMap { - newAcc.BalanceMap[key] = balanceChain.Clone() - } - } - if acc.ActionTriggers != nil { - newAcc.ActionTriggers = make(ActionTriggers, len(acc.ActionTriggers)) - for key, actionTrigger := range acc.ActionTriggers { - newAcc.ActionTriggers[key] = actionTrigger.Clone() - } - } - return newAcc -} - -// DebitConnectionFee debits the connection fee -func (acc *Account) DebitConnectionFee(cc *CallCost, usefulMoneyBalances Balances, count bool, block bool) (bool, Balance) { - var debitedBalance Balance - - if cc.deductConnectFee { - connectFee := cc.GetConnectFee() - //log.Print("CONNECT FEE: %f", connectFee) - connectFeePaid := false - for _, b := range usefulMoneyBalances { - if b.GetValue() >= connectFee { - b.SubstractValue(connectFee) - // the conect fee is not refundable! - if count { - acc.countUnits(connectFee, utils.MetaMonetary, cc, b) - } - connectFeePaid = true - debitedBalance = *b - break - } - if b.Blocker && block { // stop here - return false, debitedBalance - } - } - // debit connect fee - if connectFee > 0 && !connectFeePaid { - cc.negativeConnectFee = true - // there are no money for the connect fee; go negative - b := acc.GetDefaultMoneyBalance() - b.SubstractValue(connectFee) - debitedBalance = *b - // the conect fee is not refundable! - if count { - acc.countUnits(connectFee, utils.MetaMonetary, cc, b) - } - } - } - return true, debitedBalance -} - -func (acc *Account) matchActionFilter(condition string) (bool, error) { - sm, err := structmatcher.NewStructMatcher(condition) - if err != nil { - return false, err - } - for balanceType, balanceChain := range acc.BalanceMap { - for _, b := range balanceChain { - check, err := sm.Match(&struct { - Type string - *Balance - }{ - Type: balanceType, - Balance: b, - }) - if err != nil { - return false, err - } - if check { - return true, nil - } - } - } - return false, nil -} - -// GetID returns the account ID -func (acc *Account) GetID() string { - split := strings.Split(acc.ID, utils.ConcatenatedKeySep) - if len(split) != 2 { - return "" - } - return split[1] -} - -// AsOldStructure used in some api for transition -func (acc *Account) AsOldStructure() interface{} { - type Balance struct { - Uuid string //system wide unique - Id string // account wide unique - Value float64 - ExpirationDate time.Time - Weight float64 - DestinationIds string - RatingSubject string - Category string - SharedGroup string - Timings []*RITiming - TimingIDs string - Disabled bool - } - type Balances []*Balance - type UnitsCounter struct { - BalanceType string - // Units float64 - Balances Balances // first balance is the general one (no destination) - } - type ActionTrigger struct { - Id string - ThresholdType string - ThresholdValue float64 - Recurrent bool - MinSleep time.Duration - BalanceId string - BalanceType string - BalanceDestinationIds string - BalanceWeight float64 - BalanceExpirationDate time.Time - BalanceTimingTags string - BalanceRatingSubject string - BalanceCategory string - BalanceSharedGroup string - BalanceDisabled bool - Weight float64 - ActionsId string - MinQueuedItems int - Executed bool - } - type ActionTriggers []*ActionTrigger - type Account struct { - Id string - BalanceMap map[string]Balances - UnitCounters []*UnitsCounter - ActionTriggers ActionTriggers - AllowNegative bool - Disabled bool - } - - result := &Account{ - Id: utils.MetaOut + utils.ConcatenatedKeySep + acc.ID, - BalanceMap: make(map[string]Balances, len(acc.BalanceMap)), - UnitCounters: make([]*UnitsCounter, len(acc.UnitCounters)), - ActionTriggers: make(ActionTriggers, len(acc.ActionTriggers)), - AllowNegative: acc.AllowNegative, - Disabled: acc.Disabled, - } - for balanceType, counters := range acc.UnitCounters { - for i, uc := range counters { - if uc == nil { - continue - } - result.UnitCounters[i] = &UnitsCounter{ - BalanceType: balanceType, - Balances: make(Balances, len(uc.Counters)), - } - if len(uc.Counters) > 0 { - for j, c := range uc.Counters { - result.UnitCounters[i].Balances[j] = &Balance{ - Uuid: c.Filter.GetUuid(), - Id: c.Filter.GetID(), - Value: c.Filter.GetValue(), - ExpirationDate: c.Filter.GetExpirationDate(), - Weight: c.Filter.GetWeight(), - DestinationIds: c.Filter.GetDestinationIDs().String(), - RatingSubject: c.Filter.GetRatingSubject(), - Category: c.Filter.GetCategories().String(), - SharedGroup: c.Filter.GetSharedGroups().String(), - Timings: c.Filter.Timings, - TimingIDs: c.Filter.GetTimingIDs().String(), - Disabled: c.Filter.GetDisabled(), - } - } - } - } - } - for i, at := range acc.ActionTriggers { - b := at.Balance.CreateBalance() - result.ActionTriggers[i] = &ActionTrigger{ - Id: at.ID, - ThresholdType: at.ThresholdType, - ThresholdValue: at.ThresholdValue, - Recurrent: at.Recurrent, - MinSleep: at.MinSleep, - BalanceType: at.Balance.GetType(), - BalanceId: b.ID, - BalanceDestinationIds: b.DestinationIDs.String(), - BalanceWeight: b.Weight, - BalanceExpirationDate: b.ExpirationDate, - BalanceTimingTags: b.TimingIDs.String(), - BalanceRatingSubject: b.RatingSubject, - BalanceCategory: b.Categories.String(), - BalanceSharedGroup: b.SharedGroups.String(), - BalanceDisabled: b.Disabled, - Weight: at.Weight, - ActionsId: at.ActionsID, - MinQueuedItems: at.MinQueuedItems, - Executed: at.Executed, - } - } - for key, values := range acc.BalanceMap { - if len(values) > 0 { - key += utils.MetaOut - result.BalanceMap[key] = make(Balances, len(values)) - for i, b := range values { - result.BalanceMap[key][i] = &Balance{ - Uuid: b.Uuid, - Id: b.ID, - Value: b.Value, - ExpirationDate: b.ExpirationDate, - Weight: b.Weight, - DestinationIds: b.DestinationIDs.String(), - RatingSubject: b.RatingSubject, - Category: b.Categories.String(), - SharedGroup: b.SharedGroups.String(), - Timings: b.Timings, - TimingIDs: b.TimingIDs.String(), - Disabled: b.Disabled, - } - } - } - } - return result -} - -// AsAccountSummary converts the account into AccountSummary -func (acc *Account) AsAccountSummary() *AccountSummary { - idSplt := strings.Split(acc.ID, utils.ConcatenatedKeySep) - ad := &AccountSummary{AllowNegative: acc.AllowNegative, Disabled: acc.Disabled} - if len(idSplt) == 1 { - ad.ID = idSplt[0] - } else if len(idSplt) == 2 { - ad.Tenant = idSplt[0] - ad.ID = idSplt[1] - } - - for _, balanceType := range []string{utils.MetaData, utils.MetaSMS, utils.MetaMMS, utils.MetaVoice, utils.MetaGeneric, utils.MetaMonetary} { - balances, has := acc.BalanceMap[balanceType] - if !has { - continue - } - for _, balance := range balances { - ad.BalanceSummaries = append(ad.BalanceSummaries, balance.AsBalanceSummary(balanceType)) - } - } - return ad -} - -// Publish sends the account to stats and threshold -func (acc *Account) Publish() { - acntSummary := acc.AsAccountSummary() - cgrEv := &utils.CGREvent{ - Tenant: acntSummary.Tenant, - ID: utils.GenUUID(), - Time: utils.TimePointer(time.Now()), - Event: acntSummary.AsMapInterface(), - APIOpts: map[string]interface{}{ - utils.MetaEventType: utils.AccountUpdate, - }, - } - if len(config.CgrConfig().RalsCfg().ThresholdSConns) != 0 { - var tIDs []string - if err := connMgr.Call(config.CgrConfig().RalsCfg().ThresholdSConns, nil, - utils.ThresholdSv1ProcessEvent, &ThresholdsArgsProcessEvent{CGREvent: cgrEv}, &tIDs); err != nil && - err.Error() != utils.ErrNotFound.Error() { - utils.Logger.Warning( - fmt.Sprintf(" error: %s processing account event %+v with ThresholdS.", err.Error(), cgrEv)) - } - } - if len(config.CgrConfig().RalsCfg().StatSConns) != 0 { - var stsIDs []string - if err := connMgr.Call(config.CgrConfig().RalsCfg().StatSConns, nil, - utils.StatSv1ProcessEvent, &StatsArgsProcessEvent{CGREvent: cgrEv}, &stsIDs); err != nil && - err.Error() != utils.ErrNotFound.Error() { - utils.Logger.Warning( - fmt.Sprintf(" error: %s processing account event %+v with StatS.", err.Error(), cgrEv)) - } - } - -} - -// NewAccountSummaryFromJSON creates a new AcccountSummary from a json string -func NewAccountSummaryFromJSON(jsn string) (acntSummary *AccountSummary, err error) { - if !utils.SliceHasMember([]string{"", "null"}, jsn) { // Unmarshal only when content - err = json.Unmarshal([]byte(jsn), &acntSummary) - } - return -} - -// AccountSummary contains compressed information about an Account -type AccountSummary struct { - Tenant string - ID string - BalanceSummaries BalanceSummaries - AllowNegative bool - Disabled bool -} - -// Clone creates a copy of the structure -func (as *AccountSummary) Clone() (cln *AccountSummary) { - cln = new(AccountSummary) - cln.Tenant = as.Tenant - cln.ID = as.ID - cln.AllowNegative = as.AllowNegative - cln.Disabled = as.Disabled - if as.BalanceSummaries != nil { - cln.BalanceSummaries = make(BalanceSummaries, len(as.BalanceSummaries)) - for i, bs := range as.BalanceSummaries { - cln.BalanceSummaries[i] = new(BalanceSummary) - *cln.BalanceSummaries[i] = *bs - } - } - return -} - -// UpdateInitialValue keeps the old initial balance value -func (as *AccountSummary) UpdateInitialValue(old *AccountSummary) { - if old == nil { - return - } - for _, initialBal := range old.BalanceSummaries { - for _, currentBal := range as.BalanceSummaries { - if currentBal.UUID == initialBal.UUID { - currentBal.Initial = initialBal.Initial - break - } - } - } -} - -// GetBalanceWithID returns a Balance given balance type and balance ID -func (acc *Account) GetBalanceWithID(blcType, blcID string) (blc *Balance) { - for _, blc = range acc.BalanceMap[blcType] { - if blc.ID == blcID { - return - } - } - return nil -} - -// FieldAsInterface func to help EventCost FieldAsInterface -func (as *AccountSummary) FieldAsInterface(fldPath []string) (val interface{}, err error) { - if as == nil || len(fldPath) == 0 { - return nil, utils.ErrNotFound - } - switch fldPath[0] { - default: - opath, indx := utils.GetPathIndex(fldPath[0]) - if opath == utils.BalanceSummaries && indx != nil { - if len(as.BalanceSummaries) <= *indx { - return nil, utils.ErrNotFound - } - bl := as.BalanceSummaries[*indx] - if len(fldPath) == 1 { - return bl, nil - } - return bl.FieldAsInterface(fldPath[1:]) - } - return nil, fmt.Errorf("unsupported field prefix: <%s>", fldPath[0]) - case utils.Tenant: - if len(fldPath) != 1 { - return nil, utils.ErrNotFound - } - return as.Tenant, nil - case utils.ID: - if len(fldPath) != 1 { - return nil, utils.ErrNotFound - } - return as.ID, nil - case utils.BalanceSummaries: - if len(fldPath) == 1 { - return as.BalanceSummaries, nil - } - for _, bs := range as.BalanceSummaries { - if bs.ID == fldPath[1] { - if len(fldPath) == 2 { - return bs, nil - } - return bs.FieldAsInterface(fldPath[2:]) - } - } - return nil, utils.ErrNotFound - case utils.AllowNegative: - if len(fldPath) != 1 { - return nil, utils.ErrNotFound - } - return as.AllowNegative, nil - case utils.Disabled: - if len(fldPath) != 1 { - return nil, utils.ErrNotFound - } - return as.Disabled, nil - } -} - -func (as *AccountSummary) FieldAsString(fldPath []string) (val string, err error) { - var iface interface{} - iface, err = as.FieldAsInterface(fldPath) - if err != nil { - return - } - return utils.IfaceAsString(iface), nil -} - -// String implements utils.DataProvider -func (as *AccountSummary) String() string { - return utils.ToIJSON(as) -} - -// RemoteHost implements utils.DataProvider -func (ar *AccountSummary) RemoteHost() net.Addr { - return utils.LocalAddr() -} - -func (as *AccountSummary) AsMapInterface() map[string]interface{} { - return map[string]interface{}{ - utils.Tenant: as.Tenant, - utils.ID: as.ID, - utils.AllowNegative: as.AllowNegative, - utils.Disabled: as.Disabled, - utils.BalanceSummaries: as.BalanceSummaries, - } -} diff --git a/engine/action.go b/engine/action.go deleted file mode 100644 index 564520a55..000000000 --- a/engine/action.go +++ /dev/null @@ -1,1090 +0,0 @@ -/* -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 ( - "bytes" - "encoding/json" - "errors" - "fmt" - "html/template" - "net" - "net/http" - "net/smtp" - "reflect" - "sort" - "strconv" - "strings" - "time" - - "github.com/cgrates/cgrates/config" - "github.com/cgrates/cgrates/guardian" - "github.com/cgrates/cgrates/utils" - "github.com/cgrates/rpcclient" - "github.com/mitchellh/mapstructure" -) - -// Action will be filled for each tariff plan with the bonus value for received calls minutes. -type Action struct { - Id string - ActionType string - ExtraParameters string - Filter string - ExpirationString string // must stay as string because it can have relative values like 1month - Weight float64 - Balance *BalanceFilter - balanceValue float64 // balance value after action execution, used with cdrlog -} - -// Clone returns a clone of the action -func (a *Action) Clone() (cln *Action) { - if a == nil { - return - } - return &Action{ - Id: a.Id, - ActionType: a.ActionType, - ExtraParameters: a.ExtraParameters, - Filter: a.Filter, - ExpirationString: a.ExpirationString, - Weight: a.Weight, - Balance: a.Balance.Clone(), - } -} - -type actionTypeFunc func(*Account, *Action, Actions, interface{}) error - -func getActionFunc(typ string) (actionTypeFunc, bool) { - actionFuncMap := map[string]actionTypeFunc{ - utils.MetaLog: logAction, - utils.MetaResetTriggers: resetTriggersAction, - utils.CDRLog: cdrLogAction, - utils.MetaSetRecurrent: setRecurrentAction, - utils.MetaUnsetRecurrent: unsetRecurrentAction, - utils.MetaAllowNegative: allowNegativeAction, - utils.MetaDenyNegative: denyNegativeAction, - utils.MetaResetAccount: resetAccountAction, - utils.MetaTopUpReset: topupResetAction, - utils.MetaTopUp: topupAction, - utils.MetaDebitReset: debitResetAction, - utils.MetaDebit: debitAction, - utils.MetaResetCounters: resetCountersAction, - utils.MetaEnableAccount: enableAccountAction, - utils.MetaDisableAccount: disableAccountAction, - utils.MetaHTTPPost: callURL, - utils.HttpPostAsync: callURLAsync, - utils.MetaMailAsync: mailAsync, - utils.MetaSetDDestinations: setddestinations, - utils.MetaRemoveAccount: removeAccountAction, - utils.MetaRemoveBalance: removeBalanceAction, - utils.MetaSetBalance: setBalanceAction, - utils.MetaTransferMonetaryDefault: transferMonetaryDefaultAction, - utils.MetaCgrRpc: cgrRPCAction, - utils.TopUpZeroNegative: topupZeroNegativeAction, - utils.SetExpiry: setExpiryAction, - utils.MetaPublishAccount: publishAccount, - utils.MetaRemoveSessionCosts: removeSessionCosts, - utils.MetaRemoveExpired: removeExpired, - utils.MetaPostEvent: postEvent, - utils.MetaCDRAccount: resetAccountCDR, - utils.MetaExport: export, - utils.MetaResetThreshold: resetThreshold, - utils.MetaResetStatQueue: resetStatQueue, - utils.MetaRemoteSetAccount: remoteSetAccount, - } - f, exists := actionFuncMap[typ] - return f, exists -} - -func logAction(ub *Account, a *Action, acs Actions, extraData interface{}) (err error) { - switch { - case ub != nil: - body, _ := json.Marshal(ub) - utils.Logger.Info(fmt.Sprintf("LOG Account: %s", body)) - case extraData != nil: - body, _ := json.Marshal(extraData) - utils.Logger.Info(fmt.Sprintf("LOG ExtraData: %s", body)) - } - return -} - -func cdrLogAction(acc *Account, a *Action, acs Actions, extraData interface{}) (err error) { - if len(config.CgrConfig().SchedulerCfg().CDRsConns) == 0 { - return fmt.Errorf("No connection with CDR Server") - } - defaultTemplate := map[string]config.RSRParsers{ - utils.ToR: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.MetaAcnt+utils.NestingSep+utils.BalanceType, utils.InfieldSep), - utils.OriginHost: config.NewRSRParsersMustCompile("127.0.0.1", utils.InfieldSep), - utils.RequestType: config.NewRSRParsersMustCompile(utils.MetaNone, utils.InfieldSep), - utils.Tenant: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.MetaAcnt+utils.NestingSep+utils.Tenant, utils.InfieldSep), - utils.AccountField: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.MetaAcnt+utils.NestingSep+utils.AccountField, utils.InfieldSep), - utils.Subject: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.MetaAcnt+utils.NestingSep+utils.AccountField, utils.InfieldSep), - utils.Cost: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.MetaAct+utils.NestingSep+utils.ActionValue, utils.InfieldSep), - } - template := make(map[string]string) - // overwrite default template - if a.ExtraParameters != "" { - if err = json.Unmarshal([]byte(a.ExtraParameters), &template); err != nil { - return - } - for field, rsr := range template { - if defaultTemplate[field], err = config.NewRSRParsers(rsr, - config.CgrConfig().GeneralCfg().RSRSep); err != nil { - return - } - } - } - //In case that we have extra data we populate default templates - mapExtraData, _ := extraData.(map[string]interface{}) - for key, val := range mapExtraData { - if defaultTemplate[key], err = config.NewRSRParsers(utils.IfaceAsString(val), - config.CgrConfig().GeneralCfg().RSRSep); err != nil { - return - } - } - - // set stored cdr values - var cdrs []*CDR - for _, action := range acs { - if !utils.SliceHasMember([]string{utils.MetaDebit, utils.MetaDebitReset, utils.MetaSetBalance, utils.MetaTopUp, utils.MetaTopUpReset}, action.ActionType) || - action.Balance == nil { - continue // Only log specific actions - } - cdrLogProvider := newCdrLogProvider(acc, action) - cdr := &CDR{ - RunID: action.ActionType, - Source: utils.CDRLog, - SetupTime: time.Now(), - AnswerTime: time.Now(), - OriginID: utils.GenUUID(), - ExtraFields: make(map[string]string), - PreRated: true, - Usage: time.Duration(1), - } - cdr.CGRID = utils.Sha1(cdr.OriginID, cdr.OriginHost) - elem := reflect.ValueOf(cdr).Elem() - for key, rsrFlds := range defaultTemplate { - parsedValue, err := rsrFlds.ParseDataProvider(cdrLogProvider) - if err != nil { - return err - } - field := elem.FieldByName(key) - if field.IsValid() && field.CanSet() { - switch field.Kind() { - case reflect.Float64: - value, err := strconv.ParseFloat(parsedValue, 64) - if err != nil { - continue - } - field.SetFloat(value) - case reflect.String: - field.SetString(parsedValue) - case reflect.Int64: - value, err := strconv.ParseInt(parsedValue, 10, 64) - if err != nil { - continue - } - field.SetInt(value) - } - } else { // invalid fields go in extraFields of CDR - cdr.ExtraFields[key] = parsedValue - } - } - cdrs = append(cdrs, cdr) - var rply string - // After compute the CDR send it to CDR Server to be processed - if err := connMgr.Call(config.CgrConfig().SchedulerCfg().CDRsConns, nil, - utils.CDRsV1ProcessEvent, - &ArgV1ProcessEvent{ - Flags: []string{utils.ConcatenatedKey(utils.MetaChargers, "false")}, // do not try to get the chargers for cdrlog - CGREvent: *cdr.AsCGREvent(), - }, &rply); err != nil { - return err - } - } - b, _ := json.Marshal(cdrs) - a.ExpirationString = string(b) // testing purpose only - return -} - -func resetTriggersAction(ub *Account, a *Action, acs Actions, extraData interface{}) (err error) { - if ub == nil { - return errors.New("nil account") - } - ub.ResetActionTriggers(a) - return -} - -func setRecurrentAction(ub *Account, a *Action, acs Actions, extraData interface{}) (err error) { - if ub == nil { - return errors.New("nil account") - } - ub.SetRecurrent(a, true) - return -} - -func unsetRecurrentAction(ub *Account, a *Action, acs Actions, extraData interface{}) (err error) { - if ub == nil { - return errors.New("nil account") - } - ub.SetRecurrent(a, false) - return -} - -func allowNegativeAction(ub *Account, a *Action, acs Actions, extraData interface{}) (err error) { - if ub == nil { - return errors.New("nil account") - } - ub.AllowNegative = true - return -} - -func denyNegativeAction(ub *Account, a *Action, acs Actions, extraData interface{}) (err error) { - if ub == nil { - return errors.New("nil account") - } - ub.AllowNegative = false - return -} - -func resetAccountAction(ub *Account, a *Action, acs Actions, extraData interface{}) (err error) { - if ub == nil { - return errors.New("nil account") - } - return genericReset(ub) -} - -func topupResetAction(ub *Account, a *Action, acs Actions, extraData interface{}) (err error) { - if ub == nil { - return errors.New("nil account") - } - if ub.BalanceMap == nil { // Init the map since otherwise will get error if nil - ub.BalanceMap = make(map[string]Balances) - } - c := a.Clone() - genericMakeNegative(c) - err = genericDebit(ub, c, true) - a.balanceValue = c.balanceValue - return -} - -func topupAction(ub *Account, a *Action, acs Actions, extraData interface{}) (err error) { - if ub == nil { - return errors.New("nil account") - } - c := a.Clone() - genericMakeNegative(c) - err = genericDebit(ub, c, false) - a.balanceValue = c.balanceValue - return -} - -func debitResetAction(ub *Account, a *Action, acs Actions, extraData interface{}) (err error) { - if ub == nil { - return errors.New("nil account") - } - if ub.BalanceMap == nil { // Init the map since otherwise will get error if nil - ub.BalanceMap = make(map[string]Balances) - } - return genericDebit(ub, a, true) -} - -func debitAction(ub *Account, a *Action, acs Actions, extraData interface{}) (err error) { - if ub == nil { - return errors.New("nil account") - } - err = genericDebit(ub, a, false) - return -} - -func resetCountersAction(ub *Account, a *Action, acs Actions, extraData interface{}) (err error) { - if ub == nil { - return errors.New("nil account") - } - if ub.UnitCounters != nil { - ub.UnitCounters.resetCounters(a) - } - return -} - -func genericMakeNegative(a *Action) { - if a.Balance != nil && a.Balance.GetValue() > 0 { // only apply if not allready negative - a.Balance.SetValue(-a.Balance.GetValue()) - } -} - -func genericDebit(ub *Account, a *Action, reset bool) (err error) { - if ub == nil { - return errors.New("nil account") - } - if ub.BalanceMap == nil { - ub.BalanceMap = make(map[string]Balances) - } - return ub.debitBalanceAction(a, reset, false) -} - -func enableAccountAction(acc *Account, a *Action, acs Actions, extraData interface{}) (err error) { - if acc == nil { - return errors.New("nil account") - } - acc.Disabled = false - return -} - -func disableAccountAction(acc *Account, a *Action, acs Actions, extraData interface{}) (err error) { - if acc == nil { - return errors.New("nil account") - } - acc.Disabled = true - return -} - -/*func enableDisableBalanceAction(ub *Account, sq *CDRStatsQueueTriggered, a *Action, acs Actions) (err error) { - if ub == nil { - return errors.New("nil account") - } - ub.enableDisableBalanceAction(a) - return -}*/ - -func genericReset(ub *Account) error { - for k := range ub.BalanceMap { - ub.BalanceMap[k] = Balances{&Balance{Value: 0}} - } - ub.InitCounters() - ub.ResetActionTriggers(nil) - return nil -} - -func getOneData(ub *Account, extraData interface{}) ([]byte, error) { - switch { - case ub != nil: - return json.Marshal(ub) - case extraData != nil: - return json.Marshal(extraData) - } - return nil, nil -} - -func callURL(ub *Account, a *Action, acs Actions, extraData interface{}) error { - body, err := getOneData(ub, extraData) - if err != nil { - return err - } - pstr, err := NewHTTPPoster(config.CgrConfig().GeneralCfg().ReplyTimeout, a.ExtraParameters, - utils.ContentJSON, config.CgrConfig().GeneralCfg().PosterAttempts) - if err != nil { - return err - } - err = pstr.PostValues(body, make(http.Header)) - if err != nil && config.CgrConfig().GeneralCfg().FailedPostsDir != utils.MetaNone { - AddFailedPost(a.ExtraParameters, utils.MetaHTTPjson, utils.ActionsPoster+utils.HierarchySep+a.ActionType, body, make(map[string]interface{})) - err = nil - } - return err -} - -// Does not block for posts, no error reports -func callURLAsync(ub *Account, a *Action, acs Actions, extraData interface{}) error { - body, err := getOneData(ub, extraData) - if err != nil { - return err - } - pstr, err := NewHTTPPoster(config.CgrConfig().GeneralCfg().ReplyTimeout, a.ExtraParameters, - utils.ContentJSON, config.CgrConfig().GeneralCfg().PosterAttempts) - if err != nil { - return err - } - go func() { - err := pstr.PostValues(body, make(http.Header)) - if err != nil && config.CgrConfig().GeneralCfg().FailedPostsDir != utils.MetaNone { - AddFailedPost(a.ExtraParameters, utils.MetaHTTPjson, utils.ActionsPoster+utils.HierarchySep+a.ActionType, body, make(map[string]interface{})) - } - }() - return nil -} - -// Mails the balance hitting the threshold towards predefined list of addresses -func mailAsync(ub *Account, a *Action, acs Actions, extraData interface{}) error { - cgrCfg := config.CgrConfig() - params := strings.Split(a.ExtraParameters, string(utils.CSVSep)) - if len(params) == 0 { - return errors.New("Unconfigured parameters for mail action") - } - toAddrs := strings.Split(params[0], string(utils.FallbackSep)) - toAddrStr := "" - for idx, addr := range toAddrs { - if idx != 0 { - toAddrStr += ", " - } - toAddrStr += addr - } - var message []byte - if ub != nil { - balJsn, err := json.Marshal(ub) - if err != nil { - return err - } - message = []byte(fmt.Sprintf("To: %s\r\nSubject: [CGR Notification] Threshold hit on Balance: %s\r\n\r\nTime: \r\n\t%s\r\n\r\nBalance:\r\n\t%s\r\n\r\nYours faithfully,\r\nCGR Balance Monitor\r\n", toAddrStr, ub.ID, time.Now(), balJsn)) - } - auth := smtp.PlainAuth("", cgrCfg.MailerCfg().MailerAuthUser, cgrCfg.MailerCfg().MailerAuthPass, strings.Split(cgrCfg.MailerCfg().MailerServer, ":")[0]) // We only need host part, so ignore port - go func() { - for i := 0; i < 5; i++ { // Loop so we can increase the success rate on best effort - if err := smtp.SendMail(cgrCfg.MailerCfg().MailerServer, auth, cgrCfg.MailerCfg().MailerFromAddr, toAddrs, message); err == nil { - break - } else if i == 4 { - if ub != nil { - utils.Logger.Warning(fmt.Sprintf(" WARNING: Failed emailing, params: [%s], error: [%s], BalanceId: %s", a.ExtraParameters, err.Error(), ub.ID)) - } - break - } - time.Sleep(time.Duration(i) * time.Minute) - } - }() - return nil -} - -func setddestinations(ub *Account, a *Action, acs Actions, extraData interface{}) (err error) { - var ddcDestID string - for _, bchain := range ub.BalanceMap { - for _, b := range bchain { - for destID := range b.DestinationIDs { - if strings.HasPrefix(destID, utils.MetaDDC) { - ddcDestID = destID - break - } - } - if ddcDestID != "" { - break - } - } - if ddcDestID != "" { - break - } - } - if ddcDestID != "" { - destinations := utils.NewStringSet(nil) - for _, statID := range strings.Split(a.ExtraParameters, utils.InfieldSep) { - if statID == utils.EmptyString { - continue - } - var sts StatQueue - if err = connMgr.Call(config.CgrConfig().RalsCfg().StatSConns, nil, utils.StatSv1GetStatQueue, - &utils.TenantIDWithAPIOpts{ - TenantID: &utils.TenantID{ - Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, - ID: statID, - }, - }, &sts); err != nil { - return - } - ddcIface, has := sts.SQMetrics[utils.MetaDDC] - if !has { - continue - } - ddcMetric := ddcIface.(*StatDDC) - - // make slice from prefixes - // Review here prefixes - for p := range ddcMetric.FieldValues { - destinations.Add(p) - } - } - - newDest := &Destination{Id: ddcDestID, Prefixes: destinations.AsSlice()} - oldDest, err := dm.GetDestination(ddcDestID, true, true, utils.NonTransactional) - if err != nil { - return err - } - // update destid in storage - if err = dm.SetDestination(newDest, utils.NonTransactional); err != nil { - return err - } - if err = dm.CacheDataFromDB(utils.DestinationPrefix, []string{ddcDestID}, true); err != nil { - return err - } - - if err == nil && oldDest != nil { - if err = dm.UpdateReverseDestination(oldDest, newDest, utils.NonTransactional); err != nil { - return err - } - } - } else { - return utils.ErrNotFound - } - return nil -} - -func removeAccountAction(ub *Account, a *Action, acs Actions, extraData interface{}) error { - var accID string - if ub != nil { - accID = ub.ID - } else { - accountInfo := struct { - Tenant string - Account string - }{} - if a.ExtraParameters != "" { - if err := json.Unmarshal([]byte(a.ExtraParameters), &accountInfo); err != nil { - return err - } - } - accID = utils.ConcatenatedKey(accountInfo.Tenant, accountInfo.Account) - } - if accID == "" { - return utils.ErrInvalidKey - } - - if err := dm.RemoveAccount(accID); err != nil { - utils.Logger.Err(fmt.Sprintf("Could not remove account Id: %s: %v", accID, err)) - return err - } - - _, err := guardian.Guardian.Guard(func() (interface{}, error) { - acntAPids, err := dm.GetAccountActionPlans(accID, false, utils.NonTransactional) - if err != nil && err != utils.ErrNotFound { - utils.Logger.Err(fmt.Sprintf("Could not get action plans: %s: %v", accID, err)) - return 0, err - } - for _, apID := range acntAPids { - ap, err := dm.GetActionPlan(apID, false, utils.NonTransactional) - if err != nil { - utils.Logger.Err(fmt.Sprintf("Could not retrieve action plan: %s: %v", apID, err)) - return 0, err - } - delete(ap.AccountIDs, accID) - if err := dm.SetActionPlan(apID, ap, true, utils.NonTransactional); err != nil { - utils.Logger.Err(fmt.Sprintf("Could not save action plan: %s: %v", apID, err)) - return 0, err - } - } - if err = dm.CacheDataFromDB(utils.ActionPlanPrefix, acntAPids, true); err != nil { - return 0, err - } - if err = dm.RemAccountActionPlans(accID, nil); err != nil { - return 0, err - } - if err = dm.CacheDataFromDB(utils.AccountActionPlansPrefix, []string{accID}, true); err != nil && err.Error() != utils.ErrNotFound.Error() { - return 0, err - } - return 0, nil - - }, config.CgrConfig().GeneralCfg().LockingTimeout, utils.ActionPlanPrefix) - if err != nil { - return err - } - return nil -} - -func removeBalanceAction(ub *Account, a *Action, acs Actions, extraData interface{}) error { - if ub == nil { - return fmt.Errorf("nil account for %s action", utils.ToJSON(a)) - } - if _, exists := ub.BalanceMap[a.Balance.GetType()]; !exists { - return utils.ErrNotFound - } - bChain := ub.BalanceMap[a.Balance.GetType()] - found := false - for i := 0; i < len(bChain); i++ { - if bChain[i].MatchFilter(a.Balance, false, false) { - // delete without preserving order - bChain[i] = bChain[len(bChain)-1] - bChain = bChain[:len(bChain)-1] - i-- - found = true - } - } - ub.BalanceMap[a.Balance.GetType()] = bChain - if !found { - return utils.ErrNotFound - } - return nil -} - -func setBalanceAction(ub *Account, a *Action, acs Actions, extraData interface{}) error { - if ub == nil { - return fmt.Errorf("nil account for %s action", utils.ToJSON(a)) - } - return ub.setBalanceAction(a) -} - -func transferMonetaryDefaultAction(ub *Account, a *Action, acs Actions, extraData interface{}) error { - if ub == nil { - utils.Logger.Err("*transfer_monetary_default called without account") - return utils.ErrAccountNotFound - } - if _, exists := ub.BalanceMap[utils.MetaMonetary]; !exists { - return utils.ErrNotFound - } - defaultBalance := ub.GetDefaultMoneyBalance() - bChain := ub.BalanceMap[utils.MetaMonetary] - for _, balance := range bChain { - if balance.Uuid != defaultBalance.Uuid && - balance.ID != defaultBalance.ID && // extra caution - balance.MatchFilter(a.Balance, false, false) { - if balance.Value > 0 { - defaultBalance.Value += balance.Value - balance.Value = 0 - } - } - } - return nil -} - -// RPCRequest used by rpc action -type RPCRequest struct { - Address string - Transport string - Method string - Attempts int - Async bool - Params map[string]interface{} -} - -/* -<< .Object.Property >> - -Property can be a attribute or a method both used without () -Please also note the initial dot . - -Currently there are following objects that can be used: - -Account - the account that this action is called on -Action - the action with all it's attributs -Actions - the list of actions in the current action set -Sq - CDRStatsQueueTriggered object - -We can actually use everythiong that go templates offer. You can read more here: https://golang.org/pkg/text/template/ -*/ -func cgrRPCAction(ub *Account, a *Action, acs Actions, extraData interface{}) (err error) { - // parse template - tmpl := template.New("extra_params") - tmpl.Delims("<<", ">>") - if tmpl, err = tmpl.Parse(a.ExtraParameters); err != nil { - utils.Logger.Err(fmt.Sprintf("error parsing *cgr_rpc template: %s", err.Error())) - return - } - var buf bytes.Buffer - if err = tmpl.Execute(&buf, struct { - Account *Account - Action *Action - Actions Actions - ExtraData interface{} - }{ub, a, acs, extraData}); err != nil { - utils.Logger.Err(fmt.Sprintf("error executing *cgr_rpc template %s:", err.Error())) - return - } - var req RPCRequest - if err = json.Unmarshal(buf.Bytes(), &req); err != nil { - return - } - var params *utils.RpcParams - if params, err = utils.GetRpcParams(req.Method); err != nil { - return - } - var client rpcclient.ClientConnector - if req.Address == utils.MetaInternal { - client = params.Object.(rpcclient.ClientConnector) - } else if client, err = rpcclient.NewRPCClient(utils.TCP, req.Address, false, "", "", "", - req.Attempts, 0, config.CgrConfig().GeneralCfg().ConnectTimeout, - config.CgrConfig().GeneralCfg().ReplyTimeout, req.Transport, - nil, false, nil); err != nil { - return - } - in, out := params.InParam, params.OutParam - //utils.Logger.Info("Params: " + utils.ToJSON(req.Params)) - //p, err := utils.FromMapStringInterfaceValue(req.Params, in) - if err = mapstructure.Decode(req.Params, in); err != nil { - utils.Logger.Info("<*cgr_rpc> err: " + err.Error()) - return - } - if in == nil { - utils.Logger.Info(fmt.Sprintf("<*cgr_rpc> nil params err: req.Params: %+v params: %+v", req.Params, params)) - return utils.ErrParserError - } - utils.Logger.Info(fmt.Sprintf("<*cgr_rpc> calling: %s with: %s and result %v", req.Method, utils.ToJSON(in), out)) - if !req.Async { - err = client.Call(req.Method, in, out) - utils.Logger.Info(fmt.Sprintf("<*cgr_rpc> result: %s err: %v", utils.ToJSON(out), err)) - return - } - go func() { - err := client.Call(req.Method, in, out) - utils.Logger.Info(fmt.Sprintf("<*cgr_rpc> result: %s err: %v", utils.ToJSON(out), err)) - }() - return -} - -func topupZeroNegativeAction(ub *Account, a *Action, acs Actions, extraData interface{}) error { - if ub == nil { - return errors.New("nil account") - } - if ub.BalanceMap == nil { - ub.BalanceMap = make(map[string]Balances) - } - return ub.debitBalanceAction(a, false, true) -} - -func setExpiryAction(ub *Account, a *Action, acs Actions, extraData interface{}) error { - if ub == nil { - return errors.New("nil account") - } - balanceType := a.Balance.GetType() - for _, b := range ub.BalanceMap[balanceType] { - if b.MatchFilter(a.Balance, false, true) { - b.ExpirationDate = a.Balance.GetExpirationDate() - } - } - return nil -} - -// publishAccount will publish the account as well as each balance received to ThresholdS -func publishAccount(ub *Account, a *Action, acs Actions, extraData interface{}) error { - if ub == nil { - return errors.New("nil account") - } - ub.Publish() - return nil -} - -// Actions used to store actions according to weight -type Actions []*Action - -func (apl Actions) Len() int { - return len(apl) -} - -func (apl Actions) Swap(i, j int) { - apl[i], apl[j] = apl[j], apl[i] -} - -// we need higher weights earlyer in the list -func (apl Actions) Less(j, i int) bool { - return apl[i].Weight < apl[j].Weight -} - -// Sort used to implement sort interface -func (apl Actions) Sort() { - sort.Sort(apl) -} - -// Clone returns a clone from object -func (apl Actions) Clone() (interface{}, error) { - if apl == nil { - return nil, nil - } - cln := make(Actions, len(apl)) - for i, action := range apl { - cln[i] = action.Clone() - } - return cln, nil -} - -// newCdrLogProvider constructs a DataProvider -func newCdrLogProvider(acnt *Account, action *Action) (dP utils.DataProvider) { - dP = &cdrLogProvider{acnt: acnt, action: action, cache: utils.MapStorage{}} - return -} - -// cdrLogProvider implements utils.DataProvider so we can pass it to filters -type cdrLogProvider struct { - acnt *Account - action *Action - cache utils.MapStorage -} - -// String is part of utils.DataProvider interface -// when called, it will display the already parsed values out of cache -func (cdrP *cdrLogProvider) String() string { - return utils.ToJSON(cdrP) -} - -// FieldAsInterface is part of utils.DataProvider interface -func (cdrP *cdrLogProvider) FieldAsInterface(fldPath []string) (data interface{}, err error) { - if data, err = cdrP.cache.FieldAsInterface(fldPath); err == nil || - err != utils.ErrNotFound { // item found in cache - return - } - err = nil // cancel previous err - if len(fldPath) == 2 { - switch fldPath[0] { - case utils.MetaAcnt: - switch fldPath[1] { - case utils.AccountID: - data = cdrP.acnt.ID - case utils.Tenant: - tntAcnt := new(utils.TenantAccount) // Init with empty values - if cdrP.acnt != nil { - if tntAcnt, err = utils.NewTAFromAccountKey(cdrP.acnt.ID); err != nil { - return - } - } - data = tntAcnt.Tenant - case utils.AccountField: - tntAcnt := new(utils.TenantAccount) // Init with empty values - if cdrP.acnt != nil { - if tntAcnt, err = utils.NewTAFromAccountKey(cdrP.acnt.ID); err != nil { - return - } - } - data = tntAcnt.Account - case utils.BalanceType: - data = cdrP.action.Balance.GetType() - case utils.BalanceUUID: - data = cdrP.action.Balance.CreateBalance().Uuid - case utils.BalanceID: - data = cdrP.action.Balance.CreateBalance().ID - case utils.BalanceValue: - data = strconv.FormatFloat(cdrP.action.balanceValue, 'f', -1, 64) - case utils.DestinationIDs: - data = cdrP.action.Balance.CreateBalance().DestinationIDs.String() - case utils.ExtraParameters: - data = cdrP.action.ExtraParameters - case utils.RatingSubject: - data = cdrP.action.Balance.CreateBalance().RatingSubject - case utils.Category: - data = cdrP.action.Balance.Categories.String() - case utils.SharedGroups: - data = cdrP.action.Balance.SharedGroups.String() - } - case utils.MetaAct: - switch fldPath[1] { - case utils.ActionID: - data = cdrP.action.Id - case utils.ActionType: - data = cdrP.action.ActionType - case utils.ActionValue: - data = strconv.FormatFloat(cdrP.action.Balance.CreateBalance().GetValue(), 'f', -1, 64) - } - } - } else { - data = fldPath[0] - } - cdrP.cache.Set(fldPath, data) - return -} - -// FieldAsString is part of utils.DataProvider interface -func (cdrP *cdrLogProvider) FieldAsString(fldPath []string) (data string, err error) { - var valIface interface{} - valIface, err = cdrP.FieldAsInterface(fldPath) - if err != nil { - return - } - return utils.IfaceAsString(valIface), nil -} - -// RemoteHost is part of utils.DataProvider interface -func (cdrP *cdrLogProvider) RemoteHost() net.Addr { - return utils.LocalAddr() -} - -func removeSessionCosts(_ *Account, action *Action, _ Actions, _ interface{}) error { // FiltersID;inlineFilter - tenant := config.CgrConfig().GeneralCfg().DefaultTenant - smcFilter := new(utils.SMCostFilter) - for _, fltrID := range strings.Split(action.ExtraParameters, utils.InfieldSep) { - if len(fltrID) == 0 { - continue - } - fltr, err := dm.GetFilter(tenant, fltrID, true, true, utils.NonTransactional) - if err != nil { - utils.Logger.Warning(fmt.Sprintf("<%s> Error: %s for filter: %s in action: <%s>", - utils.Actions, err.Error(), fltrID, utils.MetaRemoveSessionCosts)) - continue - } - for _, rule := range fltr.Rules { - smcFilter, err = utils.AppendToSMCostFilter(smcFilter, rule.Type, rule.Element, rule.Values, config.CgrConfig().GeneralCfg().DefaultTimezone) - if err != nil { - utils.Logger.Warning(fmt.Sprintf("<%s> %s in action: <%s>", utils.Actions, err.Error(), utils.MetaRemoveSessionCosts)) - } - } - } - return cdrStorage.RemoveSMCosts(smcFilter) -} - -func removeExpired(acc *Account, action *Action, _ Actions, extraData interface{}) error { - if acc == nil { - return fmt.Errorf("nil account for %s action", utils.ToJSON(action)) - } - - bChain, exists := acc.BalanceMap[action.Balance.GetType()] - if !exists { - return utils.ErrNotFound - } - - found := false - for i := 0; i < len(bChain); i++ { - if bChain[i].IsExpiredAt(time.Now()) { - // delete without preserving order - bChain[i] = bChain[len(bChain)-1] - bChain = bChain[:len(bChain)-1] - i-- - found = true - } - } - acc.BalanceMap[action.Balance.GetType()] = bChain - if !found { - return utils.ErrNotFound - } - return nil -} - -func postEvent(ub *Account, a *Action, acs Actions, extraData interface{}) error { - body, err := json.Marshal(extraData) - if err != nil { - return err - } - pstr, err := NewHTTPPoster(config.CgrConfig().GeneralCfg().ReplyTimeout, a.ExtraParameters, - utils.ContentJSON, config.CgrConfig().GeneralCfg().PosterAttempts) - if err != nil { - return err - } - err = pstr.PostValues(body, make(http.Header)) - if err != nil && config.CgrConfig().GeneralCfg().FailedPostsDir != utils.MetaNone { - AddFailedPost(a.ExtraParameters, utils.MetaHTTPjson, utils.ActionsPoster+utils.HierarchySep+a.ActionType, body, make(map[string]interface{})) - err = nil - } - return err -} - -// resetAccountCDR resets the account out of values from CDR -func resetAccountCDR(ub *Account, action *Action, acts Actions, _ interface{}) error { - if ub == nil { - return errors.New("nil account") - } - if cdrStorage == nil { - return fmt.Errorf("nil cdrStorage for %s action", utils.ToJSON(action)) - } - account := ub.GetID() - filter := &utils.CDRsFilter{ - Accounts: []string{account}, - NotCosts: []float64{-1}, - OrderBy: fmt.Sprintf("%s%sdesc", utils.OrderID, utils.InfieldSep), - Paginator: utils.Paginator{Limit: utils.IntPointer(1)}, - } - cdrs, _, err := cdrStorage.GetCDRs(filter, false) - if err != nil { - return err - } - cd := cdrs[0].CostDetails - if cd == nil { - return errors.New("nil CostDetails") - } - acs := cd.AccountSummary - if acs == nil { - return errors.New("nil AccountSummary") - } - for _, bsum := range acs.BalanceSummaries { - if bsum == nil { - continue - } - if err := ub.setBalanceAction(&Action{ - Balance: &BalanceFilter{ - Uuid: &bsum.UUID, - ID: &bsum.ID, - Type: &bsum.Type, - Value: &utils.ValueFormula{Static: bsum.Value}, - Disabled: &bsum.Disabled, - }, - }); err != nil { - utils.Logger.Warning(fmt.Sprintf("<%s> Error %s setting balance %s for account: %s", utils.Actions, err, bsum.UUID, account)) - } - } - return nil -} - -func export(ub *Account, a *Action, acs Actions, extraData interface{}) (err error) { - var cgrEv *utils.CGREvent - switch { - case ub != nil: - cgrEv = &utils.CGREvent{ - Tenant: utils.NewTenantID(ub.ID).Tenant, - ID: utils.GenUUID(), - Event: map[string]interface{}{ - utils.AccountField: ub.ID, - utils.EventType: utils.AccountUpdate, - utils.EventSource: utils.AccountService, - utils.AllowNegative: ub.AllowNegative, - utils.Disabled: ub.Disabled, - utils.BalanceMap: ub.BalanceMap, - utils.UnitCounters: ub.UnitCounters, - utils.ActionTriggers: ub.ActionTriggers, - utils.UpdateTime: ub.UpdateTime, - }, - - APIOpts: map[string]interface{}{ - utils.MetaEventType: utils.AccountUpdate, - }, - } - case extraData != nil: - ev, canCast := extraData.(*utils.CGREvent) - if !canCast { - return - } - cgrEv = ev // only export CGREvents - default: - return // nothing to post - } - args := &utils.CGREventWithEeIDs{ - EeIDs: strings.Split(a.ExtraParameters, utils.InfieldSep), - CGREvent: cgrEv, - } - var rply map[string]map[string]interface{} - return connMgr.Call(config.CgrConfig().ApierCfg().EEsConns, nil, - utils.EeSv1ProcessEvent, args, &rply) -} - -func resetThreshold(ub *Account, a *Action, acs Actions, extraData interface{}) (err error) { - args := &utils.TenantIDWithAPIOpts{ - TenantID: utils.NewTenantID(a.ExtraParameters), - } - var rply string - return connMgr.Call(config.CgrConfig().SchedulerCfg().ThreshSConns, nil, - utils.ThresholdSv1ResetThreshold, args, &rply) -} - -func resetStatQueue(ub *Account, a *Action, acs Actions, extraData interface{}) (err error) { - args := &utils.TenantIDWithAPIOpts{ - TenantID: utils.NewTenantID(a.ExtraParameters), - } - var rply string - return connMgr.Call(config.CgrConfig().SchedulerCfg().StatSConns, nil, - utils.StatSv1ResetStatQueue, args, &rply) -} - -func remoteSetAccount(ub *Account, a *Action, acs Actions, extraData interface{}) (err error) { - client := &http.Client{Transport: httpPstrTransport} - var resp *http.Response - req := new(bytes.Buffer) - if err = json.NewEncoder(req).Encode(ub); err != nil { - return - } - if resp, err = client.Post(a.ExtraParameters, "application/json", req); err != nil { - return - } - acc := new(Account) - err = json.NewDecoder(resp.Body).Decode(acc) - if err != nil { - return - } - if len(acc.BalanceMap) != 0 { - *ub = *acc - } - return -} diff --git a/engine/action_plan.go b/engine/action_plan.go deleted file mode 100644 index 6bd396ae5..000000000 --- a/engine/action_plan.go +++ /dev/null @@ -1,368 +0,0 @@ -/* -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" - "sort" - "time" - - "github.com/cgrates/cgrates/config" - "github.com/cgrates/cgrates/guardian" - "github.com/cgrates/cgrates/utils" - "github.com/gorhill/cronexpr" -) - -const ( - FORMAT = "2006-1-2 15:04:05 MST" -) - -type ActionTiming struct { - Uuid string - Timing *RateInterval - ActionsID string - ExtraData interface{} - Weight float64 - actions Actions - accountIDs utils.StringMap // copy of action plans accounts - actionPlanID string // the id of the belonging action plan (info only) - stCache time.Time // cached time of the next start -} - -// Tasks converts an ActionTiming into multiple Tasks -func (at *ActionTiming) Tasks() (tsks []*Task) { - if len(at.accountIDs) == 0 { - return []*Task{{ - Uuid: at.Uuid, - ActionsID: at.ActionsID, - }} - } - tsks = make([]*Task, len(at.accountIDs)) - i := 0 - for acntID := range at.accountIDs { - tsks[i] = &Task{ - Uuid: at.Uuid, - ActionsID: at.ActionsID, - AccountID: acntID, - } - i++ - } - return -} - -type ActionPlan struct { - Id string // informative purpose only - AccountIDs utils.StringMap - ActionTimings []*ActionTiming -} - -func (apl *ActionPlan) RemoveAccountID(accID string) (found bool) { - if _, found = apl.AccountIDs[accID]; found { - delete(apl.AccountIDs, accID) - } - return -} - -// Clone clones *ActionPlan -func (apl *ActionPlan) Clone() (interface{}, error) { - cln := &ActionPlan{ - Id: apl.Id, - AccountIDs: apl.AccountIDs.Clone(), - } - if apl.ActionTimings != nil { - cln.ActionTimings = make([]*ActionTiming, len(apl.ActionTimings)) - for i, act := range apl.ActionTimings { - cln.ActionTimings[i] = act.Clone() - } - } - return cln, nil -} - -// Clone clones ActionTiming -func (at *ActionTiming) Clone() (cln *ActionTiming) { - if at == nil { - return - } - cln = &ActionTiming{ - Uuid: at.Uuid, - ActionsID: at.ActionsID, - Weight: at.Weight, - ExtraData: at.ExtraData, - Timing: at.Timing.Clone(), - } - return -} - -// getDayOrEndOfMonth returns the day if is a valid date relative to t1 month -func getDayOrEndOfMonth(day int, t1 time.Time) int { - if lastDay := utils.GetEndOfMonth(t1).Day(); lastDay <= day { // clamp the day to last day of month in order to corectly compare the time - day = lastDay - } - return day -} - -func (at *ActionTiming) GetNextStartTime(t1 time.Time) (t time.Time) { - if !at.stCache.IsZero() { - return at.stCache - } - i := at.Timing - if i == nil || i.Timing == nil { - return - } - // Normalize - if i.Timing.StartTime == "" { - i.Timing.StartTime = "00:00:00" - } - if len(i.Timing.Years) > 0 && len(i.Timing.Months) == 0 { - i.Timing.Months = append(i.Timing.Months, 1) - } - if len(i.Timing.Months) > 0 && len(i.Timing.MonthDays) == 0 { - i.Timing.MonthDays = append(i.Timing.MonthDays, 1) - } - at.stCache = cronexpr.MustParse(i.Timing.CronString()).Next(t1) - if i.Timing.ID == utils.MetaMonthlyEstimated { - // substract a month from at.stCache only if we skip 2 months - // or we skip a month because mentioned MonthDay is after the last day of the current month - if at.stCache.Month() == t1.Month()+2 || - (utils.GetEndOfMonth(t1).Day() < at.Timing.Timing.MonthDays[0] && - at.stCache.Month() == t1.Month()+1) { - lastDay := utils.GetEndOfMonth(at.stCache).Day() - // only change the time if the new one is after t1 - if tmp := at.stCache.AddDate(0, 0, -lastDay); tmp.After(t1) { - at.stCache = tmp - } - } - } - return at.stCache -} - -func (at *ActionTiming) ResetStartTimeCache() { - at.stCache = time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC) -} - -func (at *ActionTiming) SetActions(as Actions) { - at.actions = as -} - -func (at *ActionTiming) SetAccountIDs(accIDs utils.StringMap) { - at.accountIDs = accIDs -} - -func (at *ActionTiming) RemoveAccountID(acntID string) (found bool) { - if _, found = at.accountIDs[acntID]; found { - delete(at.accountIDs, acntID) - } - return -} - -func (at *ActionTiming) GetAccountIDs() utils.StringMap { - return at.accountIDs -} - -func (at *ActionTiming) SetActionPlanID(id string) { - at.actionPlanID = id -} - -func (at *ActionTiming) GetActionPlanID() string { - return at.actionPlanID -} - -func (at *ActionTiming) getActions() (as []*Action, err error) { - if at.actions == nil { - at.actions, err = dm.GetActions(at.ActionsID, false, utils.NonTransactional) - } - at.actions.Sort() - return at.actions, err -} - -// Execute will execute all actions in an action plan -// Reports on success/fail via channel if != nil -func (at *ActionTiming) Execute(successActions, failedActions chan *Action) (err error) { - at.ResetStartTimeCache() - aac, err := at.getActions() - if err != nil { - utils.Logger.Err(fmt.Sprintf("Failed to get actions for %s: %s", at.ActionsID, err)) - return - } - var partialyExecuted bool - for accID := range at.accountIDs { - _, err = guardian.Guardian.Guard(func() (interface{}, error) { - acc, err := dm.GetAccount(accID) - if err != nil { // create account - if err != utils.ErrNotFound { - utils.Logger.Warning(fmt.Sprintf("Could not get account id: %s. Skipping!", accID)) - return 0, err - } - err = nil - acc = &Account{ - ID: accID, - } - } - transactionFailed := false - removeAccountActionFound := false - for _, a := range aac { - // check action filter - if len(a.Filter) > 0 { - matched, err := acc.matchActionFilter(a.Filter) - //log.Print("Checkng: ", a.Filter, matched) - if err != nil { - return 0, err - } - if !matched { - continue - } - } - if a.Balance == nil { - a.Balance = &BalanceFilter{} - } - if a.ExpirationString != "" { // if it's *unlimited then it has to be zero time - if expDate, parseErr := utils.ParseTimeDetectLayout(a.ExpirationString, - config.CgrConfig().GeneralCfg().DefaultTimezone); parseErr == nil { - a.Balance.ExpirationDate = &time.Time{} - *a.Balance.ExpirationDate = expDate - } - } - - actionFunction, exists := getActionFunc(a.ActionType) - if !exists { - // do not allow the action plan to be rescheduled - at.Timing = nil - utils.Logger.Err(fmt.Sprintf("Function type %v not available, aborting execution!", a.ActionType)) - partialyExecuted = true - transactionFailed = true - if failedActions != nil { - go func(a *Action) { failedActions <- a }(a) - } - break - } - if err := actionFunction(acc, a, aac, at.ExtraData); err != nil { - utils.Logger.Err(fmt.Sprintf("Error executing action %s: %v!", a.ActionType, err)) - partialyExecuted = true - transactionFailed = true - if failedActions != nil { - go func(a *Action) { failedActions <- a }(a) - } - break - } - if successActions != nil { - go func(a *Action) { successActions <- a }(a) - } - if a.ActionType == utils.MetaRemoveAccount { - removeAccountActionFound = true - } - } - if !transactionFailed && !removeAccountActionFound { - dm.SetAccount(acc) - } - return 0, nil - }, config.CgrConfig().GeneralCfg().LockingTimeout, utils.AccountPrefix+accID) - } - //reset the error in case that the account is not found - err = nil - if len(at.accountIDs) == 0 { // action timing executing without accounts - for _, a := range aac { - if expDate, parseErr := utils.ParseTimeDetectLayout(a.ExpirationString, - config.CgrConfig().GeneralCfg().DefaultTimezone); (a.Balance == nil || a.Balance.EmptyExpirationDate()) && - parseErr == nil && !expDate.IsZero() { - a.Balance.ExpirationDate = &time.Time{} - *a.Balance.ExpirationDate = expDate - } - - actionFunction, exists := getActionFunc(a.ActionType) - if !exists { - // do not allow the action plan to be rescheduled - at.Timing = nil - utils.Logger.Err(fmt.Sprintf("Function type %v not available, aborting execution!", a.ActionType)) - partialyExecuted = true - if failedActions != nil { - go func(a *Action) { failedActions <- a }(a) - } - break - } - if err := actionFunction(nil, a, aac, at.ExtraData); err != nil { - utils.Logger.Err(fmt.Sprintf("Error executing accountless action %s: %v!", a.ActionType, err)) - partialyExecuted = true - if failedActions != nil { - go func(a *Action) { failedActions <- a }(a) - } - break - } - if successActions != nil { - go func(a *Action) { successActions <- a }(a) - } - } - } - if err != nil { - utils.Logger.Warning(fmt.Sprintf("Error executing action plan: %v", err)) - return err - } - if partialyExecuted { - return utils.ErrPartiallyExecuted - } - return -} - -func (at *ActionTiming) IsASAP() bool { - if at.Timing == nil { - return false - } - return at.Timing.Timing.StartTime == utils.MetaASAP -} - -// Structure to store actions according to execution time and weight -type ActionTimingPriorityList []*ActionTiming - -func (atpl ActionTimingPriorityList) Len() int { - return len(atpl) -} - -func (atpl ActionTimingPriorityList) Swap(i, j int) { - atpl[i], atpl[j] = atpl[j], atpl[i] -} - -func (atpl ActionTimingPriorityList) Less(i, j int) bool { - if atpl[i].GetNextStartTime(time.Now()).Equal(atpl[j].GetNextStartTime(time.Now())) { - // higher weights earlyer in the list - return atpl[i].Weight > atpl[j].Weight - } - return atpl[i].GetNextStartTime(time.Now()).Before(atpl[j].GetNextStartTime(time.Now())) -} - -func (atpl ActionTimingPriorityList) Sort() { - sort.Sort(atpl) -} - -// Structure to store actions according to weight -type ActionTimingWeightOnlyPriorityList []*ActionTiming - -func (atpl ActionTimingWeightOnlyPriorityList) Len() int { - return len(atpl) -} - -func (atpl ActionTimingWeightOnlyPriorityList) Swap(i, j int) { - atpl[i], atpl[j] = atpl[j], atpl[i] -} - -func (atpl ActionTimingWeightOnlyPriorityList) Less(i, j int) bool { - return atpl[i].Weight > atpl[j].Weight -} - -func (atpl ActionTimingWeightOnlyPriorityList) Sort() { - sort.Sort(atpl) -} diff --git a/engine/balances.go b/engine/balances.go index c0391ad57..3bc3fb0f6 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -46,7 +46,6 @@ type Balance struct { Factor ValueFactor Blocker bool precision int - account *Account // used to store ub reference for shared balances dirty bool } diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go deleted file mode 100644 index c83046f9d..000000000 --- a/scheduler/scheduler.go +++ /dev/null @@ -1,310 +0,0 @@ -/* -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 scheduler - -import ( - "fmt" - "sort" - "strings" - "sync" - "time" - - "github.com/cgrates/cgrates/config" - "github.com/cgrates/cgrates/engine" - "github.com/cgrates/cgrates/utils" -) - -type Scheduler struct { - sync.RWMutex - queue engine.ActionTimingPriorityList - timer *time.Timer - restartLoop chan struct{} - dm *engine.DataManager - cfg *config.CGRConfig - fltrS *engine.FilterS - schedulerStarted bool - actStatsInterval time.Duration // How long time to keep the stats in memory - actSucessChan, actFailedChan chan *engine.Action // ActionPlan will pass actions via these channels - aSMux, aFMux sync.RWMutex // protect schedStats - actSuccessStats, actFailedStats map[string]map[time.Time]bool // keep here stats regarding executed actions, map[actionType]map[execTime]bool -} - -func NewScheduler(dm *engine.DataManager, cfg *config.CGRConfig, - fltrS *engine.FilterS) (s *Scheduler) { - s = &Scheduler{ - restartLoop: make(chan struct{}), - dm: dm, - cfg: cfg, - fltrS: fltrS, - } - s.Reload() - return -} - -func (s *Scheduler) updateActStats(act *engine.Action, isFailed bool) { - mux := &s.aSMux - statsMp := s.actSuccessStats - if isFailed { - mux = &s.aFMux - statsMp = s.actFailedStats - } - now := time.Now() - mux.Lock() - for aType := range statsMp { - for t := range statsMp[aType] { - if now.Sub(t) > s.actStatsInterval { - delete(statsMp[aType], t) - if len(statsMp[aType]) == 0 { - delete(statsMp, aType) - } - } - } - } - if act == nil { - return - } - if _, hasIt := statsMp[act.ActionType]; !hasIt { - statsMp[act.ActionType] = make(map[time.Time]bool) - } - statsMp[act.ActionType][now] = true - mux.Unlock() -} - -func (s *Scheduler) Loop() { - s.schedulerStarted = true - for { - if !s.schedulerStarted { // shutdown requested - break - } - for len(s.queue) == 0 { //hang here if empty - <-s.restartLoop - } - utils.Logger.Info(fmt.Sprintf(" Scheduler queue length: %v", len(s.queue))) - s.Lock() - a0 := s.queue[0] - utils.Logger.Info(fmt.Sprintf(" Action: %s", a0.ActionsID)) - now := time.Now() - start := a0.GetNextStartTime(now) - if start.Equal(now) || start.Before(now) { - go a0.Execute(s.actSucessChan, s.actFailedChan) - // if after execute the next start time is in the past then - // do not add it to the queue - a0.ResetStartTimeCache() - now = time.Now().Add(time.Second) - start = a0.GetNextStartTime(now) - if start.Before(now) { - s.queue = s.queue[1:] - } else { - s.queue = append(s.queue, a0) - s.queue = s.queue[1:] - sort.Sort(s.queue) - } - s.Unlock() - } else { - s.Unlock() - d := a0.GetNextStartTime(now).Sub(now) - utils.Logger.Info(fmt.Sprintf(" Time to next action (%s): %v", a0.ActionsID, d)) - s.timer = time.NewTimer(d) - select { - case <-s.timer.C: - // timer has expired - utils.Logger.Info(fmt.Sprintf(" Time for action on %s", a0.ActionsID)) - case <-s.restartLoop: - // nothing to do, just continue the loop - } - } - } -} - -func (s *Scheduler) Reload() { - s.loadActionPlans() - s.restart() -} - -// loadTasks loads the tasks -// this will push the tasks that did not match -// the filters before exiting the function -func (s *Scheduler) loadTasks() { - // limit the number of concurrent tasks - limit := make(chan struct{}, 10) - // if some task don't mach the filter save them in this slice - // in oreder to push them back when finish executing them - var unexecutedTasks []*engine.Task - // execute existing tasks - for { - task, err := s.dm.DataDB().PopTask() - if err != nil || task == nil { - break - } - if pass, err := s.fltrS.Pass(s.cfg.GeneralCfg().DefaultTenant, - s.cfg.SchedulerCfg().Filters, task); err != nil || !pass { - if err != nil { - utils.Logger.Warning( - fmt.Sprintf("<%s> error: <%s> querying filters for path: <%+v>, not executing task <%s> on account <%s>", - utils.SchedulerS, err.Error(), s.cfg.SchedulerCfg().Filters, task.ActionsID, task.AccountID)) - } - // we do not push the task back as this may cause an infinite loop - // push it when the function is done and we stoped the for - // do not use defer here as the functions are exeucted - // from the last one to the first - unexecutedTasks = append(unexecutedTasks, task) - continue - } - limit <- struct{}{} - go func() { - utils.Logger.Info(fmt.Sprintf("<%s> executing task %s on account %s", - utils.SchedulerS, task.ActionsID, task.AccountID)) - task.Execute() - <-limit - }() - } - for _, t := range unexecutedTasks { - if err := s.dm.DataDB().PushTask(t); err != nil { - utils.Logger.Warning( - fmt.Sprintf("<%s> failed pushing task <%s> back to DataDB, err <%s>", - utils.SchedulerS, t.ActionsID, err.Error())) - } - } -} - -func (s *Scheduler) loadActionPlans() { - s.Lock() - defer s.Unlock() - s.loadTasks() - - actionPlans, err := s.dm.GetAllActionPlans() - if err != nil && err != utils.ErrNotFound { - utils.Logger.Warning(fmt.Sprintf(" Cannot get action plans: %v", err)) - } - utils.Logger.Info(fmt.Sprintf(" processing %d action plans", len(actionPlans))) - // recreate the queue - s.queue = engine.ActionTimingPriorityList{} - for _, actionPlan := range actionPlans { - if actionPlan == nil { - continue - } - for _, at := range actionPlan.ActionTimings { - if at.Timing == nil { - utils.Logger.Warning(fmt.Sprintf(" Nil timing on action plan: %+v, discarding!", at)) - continue - } - if at.IsASAP() { - continue // should be already executed as task - } - now := time.Now() - if at.GetNextStartTime(now).Before(now) { - // the task is obsolete, do not add it to the queue - continue - } - at.SetAccountIDs(actionPlan.AccountIDs) // copy the accounts - at.SetActionPlanID(actionPlan.Id) - for _, task := range at.Tasks() { - if pass, err := s.fltrS.Pass(s.cfg.GeneralCfg().DefaultTenant, - s.cfg.SchedulerCfg().Filters, task); err != nil { - utils.Logger.Warning( - fmt.Sprintf("<%s> error: <%s> querying filters for path: <%+v>, not executing action <%s> on account <%s>", - utils.SchedulerS, err.Error(), s.cfg.SchedulerCfg().Filters, task.ActionsID, task.AccountID)) - at.RemoveAccountID(task.AccountID) - } else if !pass { - at.RemoveAccountID(task.AccountID) - } - } - s.queue = append(s.queue, at) - } - } - sort.Sort(s.queue) - utils.Logger.Info(fmt.Sprintf(" queued %d action plans", len(s.queue))) -} - -func (s *Scheduler) restart() { - if s.schedulerStarted { - s.restartLoop <- struct{}{} - } - if s.timer != nil { - s.timer.Stop() - } -} - -type ArgsGetScheduledActions struct { - Tenant, Account *string - TimeStart, TimeEnd *time.Time // Filter based on next runTime - utils.Paginator -} - -type ScheduledAction struct { - NextRunTime time.Time - Accounts int // Number of acccounts this action will run on - ActionPlanID, ActionTimingUUID, ActionsID string -} - -func (s *Scheduler) GetScheduledActions(fltr ArgsGetScheduledActions) (schedActions []*ScheduledAction) { - s.RLock() - for _, at := range s.queue { - sas := &ScheduledAction{NextRunTime: at.GetNextStartTime(time.Now()), Accounts: len(at.GetAccountIDs()), - ActionPlanID: at.GetActionPlanID(), ActionTimingUUID: at.Uuid, ActionsID: at.ActionsID} - if fltr.TimeStart != nil && !fltr.TimeStart.IsZero() && sas.NextRunTime.Before(*fltr.TimeStart) { - continue // need to match the filter interval - } - if fltr.TimeEnd != nil && !fltr.TimeEnd.IsZero() && (sas.NextRunTime.After(*fltr.TimeEnd) || sas.NextRunTime.Equal(*fltr.TimeEnd)) { - continue - } - // filter on account - if fltr.Tenant != nil || fltr.Account != nil { - found := false - for accID := range at.GetAccountIDs() { - split := strings.Split(accID, utils.ConcatenatedKeySep) - if len(split) != 2 { - continue // malformed account id - } - if fltr.Tenant != nil && *fltr.Tenant != split[0] { - continue - } - if fltr.Account != nil && *fltr.Account != split[1] { - continue - } - found = true - break - } - if !found { - continue - } - } - schedActions = append(schedActions, sas) - } - if fltr.Paginator.Offset != nil { - if *fltr.Paginator.Offset <= len(schedActions) { - schedActions = schedActions[*fltr.Paginator.Offset:] - } - } - if fltr.Paginator.Limit != nil { - if *fltr.Paginator.Limit <= len(schedActions) { - schedActions = schedActions[:*fltr.Paginator.Limit] - } - } - s.RUnlock() - return -} - -func (s *Scheduler) Shutdown() { - s.schedulerStarted = false // disable loop on next run - s.restartLoop <- struct{}{} // cancel waiting tasks - if s.timer != nil { - s.timer.Stop() - } -} diff --git a/scheduler/scheduler_test.go b/scheduler/scheduler_test.go deleted file mode 100644 index 6c66a4cb8..000000000 --- a/scheduler/scheduler_test.go +++ /dev/null @@ -1,48 +0,0 @@ -/* -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 scheduler - -import ( - "testing" - "time" - - "github.com/cgrates/cgrates/engine" - "github.com/cgrates/cgrates/utils" -) - -func TestSchedulerUpdateActStats(t *testing.T) { - sched := &Scheduler{actStatsInterval: time.Millisecond, actSuccessStats: make(map[string]map[time.Time]bool)} - sched.updateActStats(&engine.Action{Id: "REMOVE_1", ActionType: utils.MetaRemoveAccount}, false) - if len(sched.actSuccessStats[utils.MetaRemoveAccount]) != 1 { - t.Errorf("Wrong stats: %+v", sched.actSuccessStats[utils.MetaRemoveAccount]) - } - sched.updateActStats(&engine.Action{Id: "REMOVE_2", ActionType: utils.MetaRemoveAccount}, false) - if len(sched.actSuccessStats[utils.MetaRemoveAccount]) != 2 { - t.Errorf("Wrong stats: %+v", sched.actSuccessStats[utils.MetaRemoveAccount]) - } - sched.updateActStats(&engine.Action{Id: "LOG1", ActionType: utils.MetaLog}, false) - if len(sched.actSuccessStats[utils.MetaLog]) != 1 || - len(sched.actSuccessStats[utils.MetaRemoveAccount]) != 2 { - t.Errorf("Wrong stats: %+v", sched.actSuccessStats) - } - time.Sleep(sched.actStatsInterval) - sched.updateActStats(&engine.Action{Id: "REMOVE_3", ActionType: utils.MetaRemoveAccount}, false) - if len(sched.actSuccessStats[utils.MetaRemoveAccount]) != 1 || len(sched.actSuccessStats) != 1 { - t.Errorf("Wrong stats: %+v", sched.actSuccessStats) - } -} diff --git a/services/apierv1.go b/services/apierv1.go index 0ce3f03d5..6d238b144 100644 --- a/services/apierv1.go +++ b/services/apierv1.go @@ -34,7 +34,7 @@ import ( func NewAPIerSv1Service(cfg *config.CGRConfig, dm *DataDBService, storDB *StorDBService, filterSChan chan *engine.FilterS, server *cores.Server, - schedService *SchedulerService, + actService *ActionService, responderService *ResponderService, internalAPIerSv1Chan chan rpcclient.ClientConnector, connMgr *engine.ConnManager, anz *AnalyzerService, @@ -46,7 +46,7 @@ func NewAPIerSv1Service(cfg *config.CGRConfig, dm *DataDBService, storDB: storDB, filterSChan: filterSChan, server: server, - schedService: schedService, + actService: actService, responderService: responderService, connMgr: connMgr, APIerSv1Chan: make(chan *v1.APIerSv1, 1), @@ -63,7 +63,7 @@ type APIerSv1Service struct { storDB *StorDBService filterSChan chan *engine.FilterS server *cores.Server - schedService *SchedulerService + actService *ActionService responderService *ResponderService connMgr *engine.ConnManager @@ -101,14 +101,14 @@ func (apiService *APIerSv1Service) Start() (err error) { defer apiService.Unlock() apiService.api = &v1.APIerSv1{ - DataManager: datadb, - CdrDb: stordb, - StorDb: stordb, - Config: apiService.cfg, - SchedulerService: apiService.schedService, - FilterS: filterS, - ConnMgr: apiService.connMgr, - StorDBChan: storDBChan, + DataManager: datadb, + CdrDb: stordb, + StorDb: stordb, + Config: apiService.cfg, + ActionService: apiService.actService, + FilterS: filterS, + ConnMgr: apiService.connMgr, + StorDBChan: storDBChan, Responder: apiService.responderService.GetResponder(), // if already started use it ResponderChan: respChan, // if not wait in listenAndServe diff --git a/services/schedulers.go b/services/schedulers.go deleted file mode 100644 index 0d3151c69..000000000 --- a/services/schedulers.go +++ /dev/null @@ -1,139 +0,0 @@ -/* -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 services - -import ( - "sync" - - v1 "github.com/cgrates/cgrates/apier/v1" - "github.com/cgrates/cgrates/config" - "github.com/cgrates/cgrates/cores" - "github.com/cgrates/cgrates/engine" - "github.com/cgrates/cgrates/scheduler" - "github.com/cgrates/cgrates/utils" - "github.com/cgrates/rpcclient" -) - -// NewSchedulerService returns the Scheduler Service -func NewSchedulerService(cfg *config.CGRConfig, dm *DataDBService, - cacheS *engine.CacheS, fltrSChan chan *engine.FilterS, - server *cores.Server, internalSchedulerrSChan chan rpcclient.ClientConnector, - connMgr *engine.ConnManager, anz *AnalyzerService, - srvDep map[string]*sync.WaitGroup) *SchedulerService { - return &SchedulerService{ - connChan: internalSchedulerrSChan, - cfg: cfg, - dm: dm, - cacheS: cacheS, - fltrSChan: fltrSChan, - server: server, - connMgr: connMgr, - anz: anz, - srvDep: srvDep, - } -} - -// SchedulerService implements Service interface -type SchedulerService struct { - sync.RWMutex - cfg *config.CGRConfig - dm *DataDBService - cacheS *engine.CacheS - fltrSChan chan *engine.FilterS - server *cores.Server - - schS *scheduler.Scheduler - rpc *v1.SchedulerSv1 - connChan chan rpcclient.ClientConnector - connMgr *engine.ConnManager - anz *AnalyzerService - srvDep map[string]*sync.WaitGroup -} - -// Start should handle the sercive start -func (schS *SchedulerService) Start() (err error) { - if schS.IsRunning() { - return utils.ErrServiceAlreadyRunning - } - - <-schS.cacheS.GetPrecacheChannel(utils.CacheActionPlans) // wait for ActionPlans to be cached - - fltrS := <-schS.fltrSChan - schS.fltrSChan <- fltrS - dbchan := schS.dm.GetDMChan() - datadb := <-dbchan - dbchan <- datadb - - schS.Lock() - defer schS.Unlock() - utils.Logger.Info(" Starting CGRateS Scheduler.") - schS.schS = scheduler.NewScheduler(datadb, schS.cfg, fltrS) - go schS.schS.Loop() - - schS.rpc = v1.NewSchedulerSv1(schS.cfg, datadb) - if !schS.cfg.DispatcherSCfg().Enabled { - schS.server.RpcRegister(schS.rpc) - } - schS.connChan <- schS.anz.GetInternalCodec(schS.rpc, utils.SchedulerS) - - return -} - -// Reload handles the change of config -func (schS *SchedulerService) Reload() (err error) { - schS.Lock() - schS.schS.Reload() - schS.Unlock() - return -} - -// Shutdown stops the service -func (schS *SchedulerService) Shutdown() (err error) { - schS.Lock() - schS.schS.Shutdown() - schS.schS = nil - schS.rpc = nil - <-schS.connChan - schS.Unlock() - return -} - -// IsRunning returns if the service is running -func (schS *SchedulerService) IsRunning() bool { - schS.RLock() - defer schS.RUnlock() - return schS != nil && schS.schS != nil -} - -// ServiceName returns the service name -func (schS *SchedulerService) ServiceName() string { - return utils.SchedulerS -} - -// GetScheduler returns the Scheduler -func (schS *SchedulerService) GetScheduler() *scheduler.Scheduler { - schS.RLock() - defer schS.RUnlock() - return schS.schS -} - -// ShouldRun returns if the service should be running -func (schS *SchedulerService) ShouldRun() bool { - return schS.cfg.SchedulerCfg().Enabled -} diff --git a/services/schedulers_it_test.go b/services/schedulers_it_test.go deleted file mode 100644 index d1e2d0d34..000000000 --- a/services/schedulers_it_test.go +++ /dev/null @@ -1,102 +0,0 @@ -// +build integration - -/* -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 services - -import ( - "path" - "reflect" - "sync" - "testing" - "time" - - "github.com/cgrates/cgrates/config" - "github.com/cgrates/cgrates/cores" - "github.com/cgrates/cgrates/engine" - "github.com/cgrates/cgrates/servmanager" - "github.com/cgrates/cgrates/utils" - "github.com/cgrates/rpcclient" -) - -func TestSchedulerSReload(t *testing.T) { - cfg := config.NewDefaultCGRConfig() - - utils.Logger, _ = utils.Newlogger(utils.MetaSysLog, cfg.GeneralCfg().NodeID) - utils.Logger.SetLogLevel(7) - shdChan := utils.NewSyncedChan() - shdWg := new(sync.WaitGroup) - chS := engine.NewCacheS(cfg, nil, nil) - filterSChan := make(chan *engine.FilterS, 1) - filterSChan <- nil - close(chS.GetPrecacheChannel(utils.CacheActionPlans)) - server := cores.NewServer(nil) - srvMngr := servmanager.NewServiceManager(cfg, shdChan, shdWg, nil) - srvDep := map[string]*sync.WaitGroup{utils.DataDB: new(sync.WaitGroup)} - db := NewDataDBService(cfg, nil, srvDep) - anz := NewAnalyzerService(cfg, server, filterSChan, shdChan, make(chan rpcclient.ClientConnector, 1), srvDep) - schS := NewSchedulerService(cfg, db, chS, filterSChan, server, make(chan rpcclient.ClientConnector, 1), nil, anz, srvDep) - engine.NewConnManager(cfg, nil) - srvMngr.AddServices(schS, - NewLoaderService(cfg, db, filterSChan, server, make(chan rpcclient.ClientConnector, 1), nil, anz, srvDep), db) - if err := srvMngr.StartServices(); err != nil { - t.Error(err) - } - if schS.IsRunning() { - t.Errorf("Expected service to be down") - } - if db.IsRunning() { - t.Errorf("Expected service to be down") - } - var reply string - if err := cfg.V1ReloadConfig(&config.ReloadArgs{ - Path: path.Join("/usr", "share", "cgrates", "conf", "samples", "tutmongonew"), - Section: config.SCHEDULER_JSN, - }, &reply); err != nil { - t.Error(err) - } else if reply != utils.OK { - t.Errorf("Expecting OK ,received %s", reply) - } - time.Sleep(10 * time.Millisecond) //need to switch to gorutine - if !schS.IsRunning() { - t.Errorf("Expected service to be running") - } - if !db.IsRunning() { - t.Errorf("Expected service to be running") - } - err := schS.Start() - if err == nil || err != utils.ErrServiceAlreadyRunning { - t.Errorf("\nExpecting <%+v>,\n Received <%+v>", utils.ErrServiceAlreadyRunning, err) - } - err = schS.Reload() - if err != nil { - t.Errorf("\nExpecting ,\n Received <%+v>", err) - } - getScheduler := schS.GetScheduler() - if !reflect.DeepEqual(schS.schS, getScheduler) { - t.Errorf("\nExpecting <%+v>,\n Received <%+v>", utils.ToJSON(schS.schS), utils.ToJSON(getScheduler)) - } - cfg.SchedulerCfg().Enabled = false - cfg.GetReloadChan(config.SCHEDULER_JSN) <- struct{}{} - time.Sleep(10 * time.Millisecond) - if schS.IsRunning() { - t.Errorf("Expected service to be down") - } - shdChan.CloseOnce() - time.Sleep(10 * time.Millisecond) -} diff --git a/services/schedulers_test.go b/services/schedulers_test.go deleted file mode 100644 index 85f3b9c7e..000000000 --- a/services/schedulers_test.go +++ /dev/null @@ -1,66 +0,0 @@ -/* -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 services - -import ( - "reflect" - "sync" - "testing" - - "github.com/cgrates/cgrates/scheduler" - - "github.com/cgrates/cgrates/config" - "github.com/cgrates/cgrates/cores" - "github.com/cgrates/cgrates/engine" - "github.com/cgrates/cgrates/utils" - "github.com/cgrates/rpcclient" -) - -//TestSchedulerSCoverage for cover testing -func TestSchedulerSCoverage(t *testing.T) { - cfg := config.NewDefaultCGRConfig() - shdChan := utils.NewSyncedChan() - chS := engine.NewCacheS(cfg, nil, nil) - filterSChan := make(chan *engine.FilterS, 1) - filterSChan <- nil - close(chS.GetPrecacheChannel(utils.CacheActionPlans)) - server := cores.NewServer(nil) - srvDep := map[string]*sync.WaitGroup{utils.DataDB: new(sync.WaitGroup)} - db := NewDataDBService(cfg, nil, srvDep) - anz := NewAnalyzerService(cfg, server, filterSChan, shdChan, make(chan rpcclient.ClientConnector, 1), srvDep) - schS := NewSchedulerService(cfg, db, chS, filterSChan, server, make(chan rpcclient.ClientConnector, 1), nil, anz, srvDep) - - if schS.IsRunning() { - t.Errorf("Expected service to be down") - } - schS.schS = &scheduler.Scheduler{} - if !schS.IsRunning() { - t.Errorf("Expected service to be down") - } - serviceName := schS.ServiceName() - if !reflect.DeepEqual(serviceName, utils.SchedulerS) { - t.Errorf("\nExpecting <%+v>,\n Received <%+v>", utils.SchedulerS, serviceName) - } - shouldRun := schS.ShouldRun() - if !reflect.DeepEqual(shouldRun, false) { - t.Errorf("\nExpecting ,\n Received <%+v>", shouldRun) - } - if !reflect.DeepEqual(schS.GetScheduler(), schS.schS) { - t.Errorf("\nExpecting <%+v>,\n Received <%+v>", schS.schS, schS.GetScheduler()) - } -} diff --git a/servmanager/servmanager.go b/servmanager/servmanager.go index 93bef4fde..3a33e1782 100644 --- a/servmanager/servmanager.go +++ b/servmanager/servmanager.go @@ -92,9 +92,9 @@ func (srvMngr *ServiceManager) V1StartService(args ArgStartService, reply *strin case utils.MetaScheduler: // stop the service using the config srvMngr.Lock() - srvMngr.cfg.SchedulerCfg().Enabled = true + srvMngr.cfg.ActionSCfg().Enabled = true srvMngr.Unlock() - srvMngr.cfg.GetReloadChan(config.SCHEDULER_JSN) <- struct{}{} + srvMngr.cfg.GetReloadChan(config.ActionSJson) <- struct{}{} default: err = errors.New(utils.UnsupportedServiceIDCaps) } @@ -111,9 +111,9 @@ func (srvMngr *ServiceManager) V1StopService(args ArgStartService, reply *string case utils.MetaScheduler: // stop the service using the config srvMngr.Lock() - srvMngr.cfg.SchedulerCfg().Enabled = false + srvMngr.cfg.ActionSCfg().Enabled = false srvMngr.Unlock() - srvMngr.cfg.GetReloadChan(config.SCHEDULER_JSN) <- struct{}{} + srvMngr.cfg.GetReloadChan(config.ActionSJson) <- struct{}{} default: err = errors.New(utils.UnsupportedServiceIDCaps) } @@ -198,8 +198,6 @@ func (srvMngr *ServiceManager) handleReload() { go srvMngr.reloadService(utils.ResourceS) case <-srvMngr.GetConfig().GetReloadChan(config.RouteSJson): go srvMngr.reloadService(utils.RouteS) - case <-srvMngr.GetConfig().GetReloadChan(config.SCHEDULER_JSN): - go srvMngr.reloadService(utils.SchedulerS) case <-srvMngr.GetConfig().GetReloadChan(config.RALS_JSN): go srvMngr.reloadService(utils.RALService) case <-srvMngr.GetConfig().GetReloadChan(config.ApierS):