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):