From a689916f0eb56de65c6846db63fac94fc4d7a6b3 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 7 Dec 2015 16:27:57 +0200 Subject: [PATCH 01/22] locking fix --- apier/v1/accounts.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index 4172c53e4..35f2b27d3 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -161,6 +161,7 @@ func (self *ApierV1) SetAccount(attr utils.AttrSetAccount, reply *string) error if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } + var schedulerReloadNeeded = false accId := utils.AccountKey(attr.Tenant, attr.Account) var ub *engine.Account _, err := engine.Guardian.Guard(func() (interface{}, error) { @@ -183,15 +184,12 @@ func (self *ApierV1) SetAccount(attr utils.AttrSetAccount, reply *string) error at.AccountIds = append(at.AccountIds, accId) } if len(ats) != 0 { + schedulerReloadNeeded = true if err := self.RatingDb.SetActionPlans(attr.ActionPlanId, ats); err != nil { return 0, err } // update cache self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: []string{utils.ACTION_PLAN_PREFIX + attr.ActionPlanId}}) - if self.Sched != nil { - self.Sched.LoadActionPlans(self.RatingDb) - self.Sched.Restart() - } } return 0, nil }, 0, utils.ACTION_PLAN_PREFIX) @@ -223,7 +221,13 @@ func (self *ApierV1) SetAccount(attr utils.AttrSetAccount, reply *string) error if err != nil { return utils.NewErrServerError(err) } - + if schedulerReloadNeeded { + // reload scheduler + if self.Sched != nil { + self.Sched.LoadActionPlans(self.RatingDb) + self.Sched.Restart() + } + } *reply = OK // This will mark saving of the account, error still can show up in actionTimingsId return nil } From e283f5c116f03761cdd136b6207feb95ae557bc4 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 7 Dec 2015 16:54:50 +0200 Subject: [PATCH 02/22] remove account api fixes --- apier/v1/accounts.go | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index 35f2b27d3..8f36b63a8 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -237,6 +237,7 @@ func (self *ApierV1) RemoveAccount(attr utils.AttrRemoveAccount, reply *string) return utils.NewErrMandatoryIeMissing(missing...) } accountId := utils.AccountKey(attr.Tenant, attr.Account) + var schedulerReloadNeeded bool _, err := engine.Guardian.Guard(func() (interface{}, error) { // remove it from all action plans allATs, err := self.RatingDb.GetAllActionPlans() @@ -257,10 +258,17 @@ func (self *ApierV1) RemoveAccount(attr utils.AttrRemoveAccount, reply *string) } } if changed { - // save action plan - self.RatingDb.SetActionPlans(key, ats) - // cache - self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: []string{utils.ACTION_PLAN_PREFIX + key}}) + schedulerReloadNeeded = true + _, err := engine.Guardian.Guard(func() (interface{}, error) { + // save action plan + self.RatingDb.SetActionPlans(key, ats) + // cache + self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: []string{utils.ACTION_PLAN_PREFIX + key}}) + return 0, nil + }, 0, utils.ACTION_PLAN_PREFIX) + if err != nil { + return 0, err + } } } if err := self.AccountDb.RemoveAccount(accountId); err != nil { @@ -272,7 +280,13 @@ func (self *ApierV1) RemoveAccount(attr utils.AttrRemoveAccount, reply *string) if err != nil { return utils.NewErrServerError(err) } - + if schedulerReloadNeeded { + // reload scheduler + if self.Sched != nil { + self.Sched.LoadActionPlans(self.RatingDb) + self.Sched.Restart() + } + } *reply = OK return nil } From 068a331693302c27f36b29254be2238bdaf7388b Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 7 Dec 2015 17:09:30 +0200 Subject: [PATCH 03/22] scheduler qeue console command fix --- console/scheduler_queue.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/console/scheduler_queue.go b/console/scheduler_queue.go index a409c3a68..079575373 100644 --- a/console/scheduler_queue.go +++ b/console/scheduler_queue.go @@ -58,6 +58,6 @@ func (self *CmdGetScheduledActions) PostprocessRpcParams() error { } func (self *CmdGetScheduledActions) RpcResult() interface{} { - s := v1.ScheduledActions{} + s := make([]*v1.ScheduledActions, 0) return &s } From ab316dbca0e9e5f6ac235938f04b02fcd80fb553 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 7 Dec 2015 23:24:41 +0200 Subject: [PATCH 04/22] more console commands --- apier/v1/scheduler.go | 21 ++++--- console/account_actionplan_get.go | 63 +++++++++++++++++++ ...ccountactions_set.go => actionplan_set.go} | 25 ++++---- 3 files changed, 90 insertions(+), 19 deletions(-) create mode 100644 console/account_actionplan_get.go rename console/{accountactions_set.go => actionplan_set.go} (65%) diff --git a/apier/v1/scheduler.go b/apier/v1/scheduler.go index 69871f61a..247b476b3 100644 --- a/apier/v1/scheduler.go +++ b/apier/v1/scheduler.go @@ -129,18 +129,23 @@ func (self *ApierV1) GetScheduledActions(attrs AttrsGetScheduledActions, reply * if !attrs.TimeEnd.IsZero() && (sas.NextRunTime.After(attrs.TimeEnd) || sas.NextRunTime.Equal(attrs.TimeEnd)) { continue } + // add the accounts + for _, accID := range qActions.AccountIds { + split := strings.Split(accID, utils.CONCATENATED_KEY_SEP) + if len(split) != 2 { + continue // malformed account id + } + sas.Accounts = append(sas.Accounts, &utils.TenantAccount{Tenant: split[0], Account: split[1]}) + } // filter on account if attrs.Tenant != "" || attrs.Account != "" { found := false - for _, accID := range qActions.AccountIds { - split := strings.Split(accID, utils.CONCATENATED_KEY_SEP) - if len(split) != 2 { - continue // malformed account id - } - if attrs.Tenant != "" && attrs.Tenant != split[0] { + for _, accPair := range sas.Accounts { + + if attrs.Tenant != "" && attrs.Tenant != accPair.Tenant { continue } - if attrs.Account != "" && attrs.Account != split[1] { + if attrs.Account != "" && attrs.Account != accPair.Account { continue } found = true @@ -150,7 +155,9 @@ func (self *ApierV1) GetScheduledActions(attrs AttrsGetScheduledActions, reply * continue } } + // we have a winner + schedActions = append(schedActions, sas) } if attrs.Paginator.Offset != nil { diff --git a/console/account_actionplan_get.go b/console/account_actionplan_get.go new file mode 100644 index 000000000..b478b5a62 --- /dev/null +++ b/console/account_actionplan_get.go @@ -0,0 +1,63 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2012-2015 ITsysCOM + +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/apier/v1" + +func init() { + c := &CmdGetAccountActionPlan{ + name: "account_actionplan_get", + rpcMethod: "ApierV1.GetAccountActionPlan", + rpcParams: &v1.AttrAcntAction{}, + } + commands[c.Name()] = c + c.CommandExecuter = &CommandExecuter{c} +} + +// Commander implementation +type CmdGetAccountActionPlan struct { + name string + rpcMethod string + rpcParams *v1.AttrAcntAction + *CommandExecuter +} + +func (self *CmdGetAccountActionPlan) Name() string { + return self.name +} + +func (self *CmdGetAccountActionPlan) RpcMethod() string { + return self.rpcMethod +} + +func (self *CmdGetAccountActionPlan) RpcParams(reset bool) interface{} { + if reset || self.rpcParams == nil { + self.rpcParams = &v1.AttrAcntAction{} + } + return self.rpcParams +} + +func (self *CmdGetAccountActionPlan) PostprocessRpcParams() error { + return nil +} + +func (self *CmdGetAccountActionPlan) RpcResult() interface{} { + s := make([]*v1.AccountActionTiming, 0) + return &s +} diff --git a/console/accountactions_set.go b/console/actionplan_set.go similarity index 65% rename from console/accountactions_set.go rename to console/actionplan_set.go index 69e54bd10..91fe80131 100644 --- a/console/accountactions_set.go +++ b/console/actionplan_set.go @@ -18,45 +18,46 @@ along with this program. If not, see package console -import "github.com/cgrates/cgrates/utils" +import "github.com/cgrates/cgrates/apier/v1" func init() { - c := &CmdSetAccountActions{ - name: "accountactions_set", - rpcMethod: "ApierV1.SetAccountActions", + c := &CmdSetActionPlan{ + name: "actionplan_set", + rpcMethod: "ApierV1.SetActionPlan", + rpcParams: &v1.AttrSetActionPlan{}, } commands[c.Name()] = c c.CommandExecuter = &CommandExecuter{c} } // Commander implementation -type CmdSetAccountActions struct { +type CmdSetActionPlan struct { name string rpcMethod string - rpcParams *utils.TPAccountActions + rpcParams *v1.AttrSetActionPlan *CommandExecuter } -func (self *CmdSetAccountActions) Name() string { +func (self *CmdSetActionPlan) Name() string { return self.name } -func (self *CmdSetAccountActions) RpcMethod() string { +func (self *CmdSetActionPlan) RpcMethod() string { return self.rpcMethod } -func (self *CmdSetAccountActions) RpcParams(reset bool) interface{} { +func (self *CmdSetActionPlan) RpcParams(reset bool) interface{} { if reset || self.rpcParams == nil { - self.rpcParams = &utils.TPAccountActions{} + self.rpcParams = &v1.AttrSetActionPlan{} } return self.rpcParams } -func (self *CmdSetAccountActions) PostprocessRpcParams() error { +func (self *CmdSetActionPlan) PostprocessRpcParams() error { return nil } -func (self *CmdSetAccountActions) RpcResult() interface{} { +func (self *CmdSetActionPlan) RpcResult() interface{} { var s string return &s } From 2aedbcdff4df3021984464b0b3e3156db38964dc Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 8 Dec 2015 12:54:19 +0200 Subject: [PATCH 05/22] added get action plan api + console --- apier/v1/accounts.go | 2 +- apier/v1/apier.go | 25 +++++++++++++++ console/actionplan_get.go | 66 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 console/actionplan_get.go diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index 8f36b63a8..087cb614e 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -252,7 +252,7 @@ func (self *ApierV1) RemoveAccount(attr utils.AttrRemoveAccount, reply *string) // delete without preserving order at.AccountIds[i] = at.AccountIds[len(at.AccountIds)-1] at.AccountIds = at.AccountIds[:len(at.AccountIds)-1] - i -= 1 + i-- changed = true } } diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 38157f5a8..15ce611c9 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -775,6 +775,31 @@ func (self *ApierV1) SetActionPlan(attrs AttrSetActionPlan, reply *string) error return nil } +type AttrGetActionPlan struct { + Id string +} + +func (self *ApierV1) GetActionPlan(attr AttrGetActionPlan, reply *[]engine.ActionPlans) error { + var result []engine.ActionPlans + if attr.Id == "" || attr.Id == "*" { + aplsMap, err := self.RatingDb.GetAllActionPlans() + if err != nil { + return err + } + for _, apls := range aplsMap { + result = append(result, apls) + } + } else { + apls, err := self.RatingDb.GetActionPlans(attr.Id, false) + if err != nil { + return err + } + result = append(result, apls) + } + *reply = result + return nil +} + type AttrAddActionTrigger struct { ActionTriggersId string Tenant string diff --git a/console/actionplan_get.go b/console/actionplan_get.go new file mode 100644 index 000000000..af17a0aed --- /dev/null +++ b/console/actionplan_get.go @@ -0,0 +1,66 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2012-2015 ITsysCOM + +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/apier/v1" + "github.com/cgrates/cgrates/engine" +) + +func init() { + c := &CmdGetActionPlan{ + name: "actionplan_get", + rpcMethod: "ApierV1.GetActionPlan", + rpcParams: &v1.AttrGetActionPlan{}, + } + commands[c.Name()] = c + c.CommandExecuter = &CommandExecuter{c} +} + +// Commander implementation +type CmdGetActionPlan struct { + name string + rpcMethod string + rpcParams *v1.AttrGetActionPlan + *CommandExecuter +} + +func (self *CmdGetActionPlan) Name() string { + return self.name +} + +func (self *CmdGetActionPlan) RpcMethod() string { + return self.rpcMethod +} + +func (self *CmdGetActionPlan) RpcParams(reset bool) interface{} { + if reset || self.rpcParams == nil { + self.rpcParams = &v1.AttrGetActionPlan{} + } + return self.rpcParams +} + +func (self *CmdGetActionPlan) PostprocessRpcParams() error { + return nil +} + +func (self *CmdGetActionPlan) RpcResult() interface{} { + s := make([]*engine.ActionPlans, 0) + return &s +} From 9a10b19d1732b4c189ce7b82d02c86d19d1da0ae Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 8 Dec 2015 15:47:10 +0200 Subject: [PATCH 06/22] more consicse get scheduler queue --- apier/v1/scheduler.go | 23 +++++++++-------------- utils/apitpdata.go | 4 ---- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/apier/v1/scheduler.go b/apier/v1/scheduler.go index 247b476b3..0f34bae39 100644 --- a/apier/v1/scheduler.go +++ b/apier/v1/scheduler.go @@ -105,7 +105,7 @@ type AttrsGetScheduledActions struct { type ScheduledActions struct { NextRunTime time.Time - Accounts []*utils.TenantAccount + Accounts int ActionsId, ActionPlanId, ActionPlanUuid string } @@ -116,7 +116,7 @@ func (self *ApierV1) GetScheduledActions(attrs AttrsGetScheduledActions, reply * schedActions := make([]*ScheduledActions, 0) // needs to be initialized if remains empty scheduledActions := self.Sched.GetQueue() for _, qActions := range scheduledActions { - sas := &ScheduledActions{ActionsId: qActions.ActionsId, ActionPlanId: qActions.Id, ActionPlanUuid: qActions.Uuid} + sas := &ScheduledActions{ActionsId: qActions.ActionsId, ActionPlanId: qActions.Id, ActionPlanUuid: qActions.Uuid, Accounts: len(qActions.ActionsId)} if attrs.SearchTerm != "" && !(strings.Contains(sas.ActionPlanId, attrs.SearchTerm) || strings.Contains(sas.ActionsId, attrs.SearchTerm)) { @@ -129,23 +129,18 @@ func (self *ApierV1) GetScheduledActions(attrs AttrsGetScheduledActions, reply * if !attrs.TimeEnd.IsZero() && (sas.NextRunTime.After(attrs.TimeEnd) || sas.NextRunTime.Equal(attrs.TimeEnd)) { continue } - // add the accounts - for _, accID := range qActions.AccountIds { - split := strings.Split(accID, utils.CONCATENATED_KEY_SEP) - if len(split) != 2 { - continue // malformed account id - } - sas.Accounts = append(sas.Accounts, &utils.TenantAccount{Tenant: split[0], Account: split[1]}) - } // filter on account if attrs.Tenant != "" || attrs.Account != "" { found := false - for _, accPair := range sas.Accounts { - - if attrs.Tenant != "" && attrs.Tenant != accPair.Tenant { + for _, accID := range qActions.AccountIds { + split := strings.Split(accID, utils.CONCATENATED_KEY_SEP) + if len(split) != 2 { + continue // malformed account id + } + if attrs.Tenant != "" && attrs.Tenant != split[0] { continue } - if attrs.Account != "" && attrs.Account != accPair.Account { + if attrs.Account != "" && attrs.Account != split[1] { continue } found = true diff --git a/utils/apitpdata.go b/utils/apitpdata.go index 72e2d8c7f..8e40df14e 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -800,10 +800,6 @@ func NewTAFromAccountKey(accountKey string) (*TenantAccount, error) { return &TenantAccount{accountSplt[0], accountSplt[1]}, nil } -type TenantAccount struct { - Tenant, Account string -} - func NewDTCSFromRPKey(rpKey string) (*DirectionTenantCategorySubject, error) { rpSplt := strings.Split(rpKey, CONCATENATED_KEY_SEP) if len(rpSplt) != 4 { From ceb8e493de0d3f6e14d86bc2c92ed34e0f1d9307 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 8 Dec 2015 15:53:17 +0200 Subject: [PATCH 07/22] build fix --- utils/apitpdata.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/utils/apitpdata.go b/utils/apitpdata.go index 8e40df14e..e474b8bba 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -792,14 +792,6 @@ type AttrDerivedChargers struct { Direction, Tenant, Category, Account, Subject, Destination string } -func NewTAFromAccountKey(accountKey string) (*TenantAccount, error) { - accountSplt := strings.Split(accountKey, CONCATENATED_KEY_SEP) - if len(accountSplt) != 2 { - return nil, fmt.Errorf("Unsupported format for TenantAccount: %s", accountKey) - } - return &TenantAccount{accountSplt[0], accountSplt[1]}, nil -} - func NewDTCSFromRPKey(rpKey string) (*DirectionTenantCategorySubject, error) { rpSplt := strings.Split(rpKey, CONCATENATED_KEY_SEP) if len(rpSplt) != 4 { From ac3d735b66ddf66cd3f30d283f92c12f8bdb57c2 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 8 Dec 2015 15:53:59 +0200 Subject: [PATCH 08/22] build fix again --- utils/apitpdata.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/utils/apitpdata.go b/utils/apitpdata.go index e474b8bba..72e2d8c7f 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -792,6 +792,18 @@ type AttrDerivedChargers struct { Direction, Tenant, Category, Account, Subject, Destination string } +func NewTAFromAccountKey(accountKey string) (*TenantAccount, error) { + accountSplt := strings.Split(accountKey, CONCATENATED_KEY_SEP) + if len(accountSplt) != 2 { + return nil, fmt.Errorf("Unsupported format for TenantAccount: %s", accountKey) + } + return &TenantAccount{accountSplt[0], accountSplt[1]}, nil +} + +type TenantAccount struct { + Tenant, Account string +} + func NewDTCSFromRPKey(rpKey string) (*DirectionTenantCategorySubject, error) { rpSplt := strings.Split(rpKey, CONCATENATED_KEY_SEP) if len(rpSplt) != 4 { From 63aae362af36c4a141a0190aa6b836e8411304e1 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 8 Dec 2015 16:19:51 +0200 Subject: [PATCH 09/22] count accounts instead of full list --- apier/v1/scheduler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apier/v1/scheduler.go b/apier/v1/scheduler.go index 0f34bae39..ab4d0b9c4 100644 --- a/apier/v1/scheduler.go +++ b/apier/v1/scheduler.go @@ -116,7 +116,7 @@ func (self *ApierV1) GetScheduledActions(attrs AttrsGetScheduledActions, reply * schedActions := make([]*ScheduledActions, 0) // needs to be initialized if remains empty scheduledActions := self.Sched.GetQueue() for _, qActions := range scheduledActions { - sas := &ScheduledActions{ActionsId: qActions.ActionsId, ActionPlanId: qActions.Id, ActionPlanUuid: qActions.Uuid, Accounts: len(qActions.ActionsId)} + sas := &ScheduledActions{ActionsId: qActions.ActionsId, ActionPlanId: qActions.Id, ActionPlanUuid: qActions.Uuid, Accounts: len(qActions.AccountIds)} if attrs.SearchTerm != "" && !(strings.Contains(sas.ActionPlanId, attrs.SearchTerm) || strings.Contains(sas.ActionsId, attrs.SearchTerm)) { From 59664c553bd989e3a9e43a565fb4b2b12cc41836 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 8 Dec 2015 17:32:40 +0200 Subject: [PATCH 10/22] make sure next action time is refreshed --- engine/action_plan.go | 6 +++--- engine/actions_test.go | 11 +++++++++++ scheduler/scheduler.go | 8 +++++--- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/engine/action_plan.go b/engine/action_plan.go index 0443ebab2..6bdea2c4c 100644 --- a/engine/action_plan.go +++ b/engine/action_plan.go @@ -218,7 +218,7 @@ YEARS: return } -func (at *ActionPlan) resetStartTimeCache() { +func (at *ActionPlan) ResetStartTimeCache() { at.stCache = time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC) } @@ -238,7 +238,7 @@ func (at *ActionPlan) Execute() (err error) { if len(at.AccountIds) == 0 { // nothing to do if no accounts set return } - at.resetStartTimeCache() + at.ResetStartTimeCache() aac, err := at.getActions() if err != nil { utils.Logger.Err(fmt.Sprintf("Failed to get actions for %s: %s", at.ActionsId, err)) @@ -283,7 +283,7 @@ func (at *ActionPlan) Execute() (err error) { // delete without preserving order at.AccountIds[i] = at.AccountIds[len(at.AccountIds)-1] at.AccountIds = at.AccountIds[:len(at.AccountIds)-1] - i -= 1 + i-- changed = true } } diff --git a/engine/actions_test.go b/engine/actions_test.go index d3799f35d..a50afce89 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -56,6 +56,17 @@ func TestActionPlanNothing(t *testing.T) { } } +func TestActionTimingMidnight(t *testing.T) { + at := &ActionPlan{Timing: &RateInterval{Timing: &RITiming{StartTime: "00:00:00"}}} + y, m, d := referenceDate.Date() + now := time.Date(y, m, d, 0, 0, 1, 0, time.Local) + st := at.GetNextStartTime(now) + expected := time.Date(y, m, d, 0, 0, 0, 0, time.Local).AddDate(0, 0, 1) + if !st.Equal(expected) { + t.Errorf("Expected %v was %v", expected, st) + } +} + func TestActionPlanOnlyHour(t *testing.T) { at := &ActionPlan{Timing: &RateInterval{Timing: &RITiming{StartTime: "10:01:00"}}} st := at.GetNextStartTime(referenceDate) diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index 0493fb6dd..95a72c1fd 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -46,13 +46,15 @@ func (s *Scheduler) Loop() { } s.Lock() a0 := s.queue[0] - //utils.Logger.Info(fmt.Sprintf("Scheduler qeue length: %v", len(s.qeue))) + utils.Logger.Info(fmt.Sprintf(" Action: %s", a0.Id)) + utils.Logger.Info(fmt.Sprintf(" Scheduler queue length: %v", len(s.queue))) now := time.Now() start := a0.GetNextStartTime(now) if start.Equal(now) || start.Before(now) { go a0.Execute() // 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) { @@ -71,7 +73,7 @@ func (s *Scheduler) Loop() { select { case <-s.timer.C: // timer has expired - utils.Logger.Info(fmt.Sprintf(" Time for action on %v", a0)) + utils.Logger.Info(fmt.Sprintf(" Time for action on %v", a0.Id)) case <-s.restartLoop: // nothing to do, just continue the loop } @@ -87,6 +89,7 @@ func (s *Scheduler) LoadActionPlans(storage engine.RatingStorage) { utils.Logger.Info(fmt.Sprintf(" processing %d action plans", len(actionPlans))) // recreate the queue s.Lock() + defer s.Unlock() s.queue = engine.ActionPlanPriotityList{} for key, aps := range actionPlans { toBeSaved := false @@ -128,7 +131,6 @@ func (s *Scheduler) LoadActionPlans(storage engine.RatingStorage) { } sort.Sort(s.queue) utils.Logger.Info(fmt.Sprintf(" queued %d action plans", len(s.queue))) - s.Unlock() } func (s *Scheduler) Restart() { From 6b2e32944c34cacf04b04751939e7ce5f430500d Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 9 Dec 2015 15:36:31 +0200 Subject: [PATCH 11/22] flood protection for scheduler --- apier/v1/accounts.go | 9 ++-- apier/v1/apier.go | 15 ++---- apier/v2/apier.go | 6 +-- cmd/cgr-engine/cgr-engine.go | 4 +- cmd/cgr-engine/registration.go | 4 +- console/{account_add.go => account_set.go} | 2 +- engine/rateinterval.go | 5 +- general_tests/ddazmbl1_test.go | 4 +- general_tests/ddazmbl2_test.go | 2 +- general_tests/ddazmbl3_test.go | 2 +- scheduler/scheduler.go | 56 ++++++++++++++++++---- 11 files changed, 67 insertions(+), 42 deletions(-) rename console/{account_add.go => account_set.go} (98%) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index 087cb614e..f0213d955 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -99,8 +99,7 @@ func (self *ApierV1) RemActionTiming(attrs AttrRemActionTiming, reply *string) e return utils.NewErrServerError(err) } if attrs.ReloadScheduler && self.Sched != nil { - self.Sched.LoadActionPlans(self.RatingDb) - self.Sched.Restart() + self.Sched.Reload(true) } *reply = OK return nil @@ -224,8 +223,7 @@ func (self *ApierV1) SetAccount(attr utils.AttrSetAccount, reply *string) error if schedulerReloadNeeded { // reload scheduler if self.Sched != nil { - self.Sched.LoadActionPlans(self.RatingDb) - self.Sched.Restart() + self.Sched.Reload(true) } } *reply = OK // This will mark saving of the account, error still can show up in actionTimingsId @@ -283,8 +281,7 @@ func (self *ApierV1) RemoveAccount(attr utils.AttrRemoveAccount, reply *string) if schedulerReloadNeeded { // reload scheduler if self.Sched != nil { - self.Sched.LoadActionPlans(self.RatingDb) - self.Sched.Restart() + self.Sched.Reload(true) } } *reply = OK diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 15ce611c9..2bf808db5 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -519,8 +519,7 @@ func (self *ApierV1) LoadTariffPlanFromStorDb(attrs AttrLoadTpFromStorDb, reply if len(aps) != 0 && self.Sched != nil { utils.Logger.Info("ApierV1.LoadTariffPlanFromStorDb, reloading scheduler.") - self.Sched.LoadActionPlans(self.RatingDb) - self.Sched.Restart() + self.Sched.Reload(true) } if len(cstKeys) != 0 && self.CdrStatsSrv != nil { @@ -768,8 +767,7 @@ func (self *ApierV1) SetActionPlan(attrs AttrSetActionPlan, reply *string) error if self.Sched == nil { return errors.New("SCHEDULER_NOT_ENABLED") } - self.Sched.LoadActionPlans(self.RatingDb) - self.Sched.Restart() + self.Sched.Reload(true) } *reply = OK return nil @@ -952,8 +950,7 @@ func (self *ApierV1) LoadAccountActions(attrs utils.TPAccountActions, reply *str return err } if self.Sched != nil { - self.Sched.LoadActionPlans(self.RatingDb) - self.Sched.Restart() + self.Sched.Reload(true) } *reply = OK return nil @@ -963,8 +960,7 @@ func (self *ApierV1) ReloadScheduler(input string, reply *string) error { if self.Sched == nil { return utils.ErrNotFound } - self.Sched.LoadActionPlans(self.RatingDb) - self.Sched.Restart() + self.Sched.Reload(true) *reply = OK return nil } @@ -1208,8 +1204,7 @@ func (self *ApierV1) LoadTariffPlanFromFolder(attrs utils.AttrLoadTpFromFolder, } if len(aps) != 0 && self.Sched != nil { utils.Logger.Info("ApierV1.LoadTariffPlanFromFolder, reloading scheduler.") - self.Sched.LoadActionPlans(self.RatingDb) - self.Sched.Restart() + self.Sched.Reload(true) } if len(cstKeys) != 0 && self.CdrStatsSrv != nil { if err := self.CdrStatsSrv.ReloadQueues(cstKeys, nil); err != nil { diff --git a/apier/v2/apier.go b/apier/v2/apier.go index e35c48a7d..90b146480 100644 --- a/apier/v2/apier.go +++ b/apier/v2/apier.go @@ -91,8 +91,7 @@ func (self *ApierV2) LoadAccountActions(attrs AttrLoadAccountActions, reply *str return err } if self.Sched != nil { - self.Sched.LoadActionPlans(self.RatingDb) - self.Sched.Restart() + self.Sched.Reload(true) } *reply = v1.OK return nil @@ -249,8 +248,7 @@ func (self *ApierV2) LoadTariffPlanFromFolder(attrs utils.AttrLoadTpFromFolder, } if len(aps) != 0 && self.Sched != nil { utils.Logger.Info("ApierV1.LoadTariffPlanFromFolder, reloading scheduler.") - self.Sched.LoadActionPlans(self.RatingDb) - self.Sched.Restart() + self.Sched.Reload(true) } if len(cstKeys) != 0 && self.CdrStatsSrv != nil { if err := self.CdrStatsSrv.ReloadQueues(cstKeys, nil); err != nil { diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go index 3e819fa82..452b8cda5 100644 --- a/cmd/cgr-engine/cgr-engine.go +++ b/cmd/cgr-engine/cgr-engine.go @@ -489,11 +489,11 @@ func startScheduler(internalSchedulerChan chan *scheduler.Scheduler, cacheDoneCh cacheDone := <-cacheDoneChan cacheDoneChan <- cacheDone utils.Logger.Info("Starting CGRateS Scheduler.") - sched := scheduler.NewScheduler() + sched := scheduler.NewScheduler(ratingDb) go reloadSchedulerSingnalHandler(sched, ratingDb) time.Sleep(1) internalSchedulerChan <- sched - sched.LoadActionPlans(ratingDb) + sched.Reload(true) sched.Loop() exitChan <- true // Should not get out of loop though } diff --git a/cmd/cgr-engine/registration.go b/cmd/cgr-engine/registration.go index ec856d6f5..985d469bb 100644 --- a/cmd/cgr-engine/registration.go +++ b/cmd/cgr-engine/registration.go @@ -125,9 +125,7 @@ func reloadSchedulerSingnalHandler(sched *scheduler.Scheduler, getter engine.Rat sig := <-c utils.Logger.Info(fmt.Sprintf("Caught signal %v, reloading action timings.\n", sig)) - sched.LoadActionPlans(getter) - // check the tip of the queue for new actions - sched.Restart() + sched.Reload(true) } } diff --git a/console/account_add.go b/console/account_set.go similarity index 98% rename from console/account_add.go rename to console/account_set.go index 5875a4fce..d81b0c38e 100644 --- a/console/account_add.go +++ b/console/account_set.go @@ -22,7 +22,7 @@ import "github.com/cgrates/cgrates/utils" func init() { c := &CmdAddAccount{ - name: "account_add", + name: "account_set", rpcMethod: "ApierV1.SetAccount", } commands[c.Name()] = c diff --git a/engine/rateinterval.go b/engine/rateinterval.go index e11f731e3..9a330d029 100644 --- a/engine/rateinterval.go +++ b/engine/rateinterval.go @@ -58,10 +58,11 @@ func (rit *RITiming) CronString() string { hour, min, sec = "*", "*", "*" } else { hms := strings.Split(rit.StartTime, ":") - if len(hms) != 3 { + if len(hms) == 3 { + hour, min, sec = hms[0], hms[1], hms[2] + } else { hour, min, sec = "*", "*", "*" } - hour, min, sec = hms[0], hms[1], hms[2] if strings.HasPrefix(hour, "0") { hour = hour[1:] } diff --git a/general_tests/ddazmbl1_test.go b/general_tests/ddazmbl1_test.go index 34c8a2988..7155f41da 100644 --- a/general_tests/ddazmbl1_test.go +++ b/general_tests/ddazmbl1_test.go @@ -129,8 +129,8 @@ TOPUP10_AT,TOPUP10_AC1,ASAP,10` } func TestExecuteActions(t *testing.T) { - scheduler.NewScheduler().LoadActionPlans(ratingDb) - time.Sleep(time.Millisecond) // Give time to scheduler to topup the account + scheduler.NewScheduler(ratingDb).Reload(false) + time.Sleep(time.Second) // Give time to scheduler to topup the account if acnt, err := acntDb.GetAccount("cgrates.org:12344"); err != nil { t.Error(err) } else if len(acnt.BalanceMap) != 2 { diff --git a/general_tests/ddazmbl2_test.go b/general_tests/ddazmbl2_test.go index 1a0004d1a..eab079787 100644 --- a/general_tests/ddazmbl2_test.go +++ b/general_tests/ddazmbl2_test.go @@ -128,7 +128,7 @@ TOPUP10_AT,TOPUP10_AC1,ASAP,10` } func TestExecuteActions2(t *testing.T) { - scheduler.NewScheduler().LoadActionPlans(ratingDb2) + scheduler.NewScheduler(ratingDb2).Reload(false) time.Sleep(time.Millisecond) // Give time to scheduler to topup the account if acnt, err := acntDb2.GetAccount("cgrates.org:12345"); err != nil { t.Error(err) diff --git a/general_tests/ddazmbl3_test.go b/general_tests/ddazmbl3_test.go index 0b49f8ea7..0ba5d7a84 100644 --- a/general_tests/ddazmbl3_test.go +++ b/general_tests/ddazmbl3_test.go @@ -126,7 +126,7 @@ RP_UK,DR_UK_Mobile_BIG5,ALWAYS,10` } func TestExecuteActions3(t *testing.T) { - scheduler.NewScheduler().LoadActionPlans(ratingDb3) + scheduler.NewScheduler(ratingDb3).Reload(false) time.Sleep(time.Millisecond) // Give time to scheduler to topup the account if acnt, err := acntDb3.GetAccount("cgrates.org:12346"); err != nil { t.Error(err) diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index 95a72c1fd..7a891def1 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -33,21 +33,30 @@ type Scheduler struct { timer *time.Timer restartLoop chan bool sync.Mutex + storage engine.RatingStorage + waitingReload bool + loopChecker chan int + schedulerStarted bool } -func NewScheduler() *Scheduler { - return &Scheduler{restartLoop: make(chan bool)} +func NewScheduler(storage engine.RatingStorage) *Scheduler { + return &Scheduler{ + restartLoop: make(chan bool), + storage: storage, + loopChecker: make(chan int), + } } func (s *Scheduler) Loop() { + s.schedulerStarted = true for { 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.Id)) - utils.Logger.Info(fmt.Sprintf(" Scheduler queue length: %v", len(s.queue))) now := time.Now() start := a0.GetNextStartTime(now) if start.Equal(now) || start.Before(now) { @@ -81,15 +90,40 @@ func (s *Scheduler) Loop() { } } -func (s *Scheduler) LoadActionPlans(storage engine.RatingStorage) { - actionPlans, err := storage.GetAllActionPlans() +func (s *Scheduler) Reload(protect bool) { + s.Lock() + defer s.Unlock() + + if protect { + if s.waitingReload { + s.loopChecker <- 1 + } + s.waitingReload = true + go func() { + t := time.NewTicker(time.Second) // wait 1 second before start + select { + case <-s.loopChecker: + t.Stop() // cancel reload + case <-t.C: + s.LoadActionPlans() + s.Restart() + t.Stop() + s.waitingReload = false + } + }() + } else { + s.LoadActionPlans() + s.Restart() + } +} + +func (s *Scheduler) LoadActionPlans() { + actionPlans, err := s.storage.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.Lock() - defer s.Unlock() s.queue = engine.ActionPlanPriotityList{} for key, aps := range actionPlans { toBeSaved := false @@ -123,8 +157,8 @@ func (s *Scheduler) LoadActionPlans(storage engine.RatingStorage) { } if toBeSaved { engine.Guardian.Guard(func() (interface{}, error) { - storage.SetActionPlans(key, newApls) - storage.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: []string{utils.ACTION_PLAN_PREFIX + key}}) + s.storage.SetActionPlans(key, newApls) + s.storage.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: []string{utils.ACTION_PLAN_PREFIX + key}}) return 0, nil }, 0, utils.ACTION_PLAN_PREFIX) } @@ -134,7 +168,9 @@ func (s *Scheduler) LoadActionPlans(storage engine.RatingStorage) { } func (s *Scheduler) Restart() { - s.restartLoop <- true + if s.schedulerStarted { + s.restartLoop <- true + } if s.timer != nil { s.timer.Stop() } From 1d1c8f99c562fb9a959a0127e094556e5d98db54 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 9 Dec 2015 15:44:59 +0200 Subject: [PATCH 12/22] made some methods private and extra locking --- scheduler/scheduler.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index 7a891def1..bbbd34a71 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -105,25 +105,27 @@ func (s *Scheduler) Reload(protect bool) { case <-s.loopChecker: t.Stop() // cancel reload case <-t.C: - s.LoadActionPlans() - s.Restart() + s.loadActionPlans() + s.restart() t.Stop() s.waitingReload = false } }() } else { - s.LoadActionPlans() - s.Restart() + s.loadActionPlans() + s.restart() } } -func (s *Scheduler) LoadActionPlans() { +func (s *Scheduler) loadActionPlans() { actionPlans, err := s.storage.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.Lock() + defer s.Unlock() s.queue = engine.ActionPlanPriotityList{} for key, aps := range actionPlans { toBeSaved := false @@ -167,7 +169,7 @@ func (s *Scheduler) LoadActionPlans() { utils.Logger.Info(fmt.Sprintf(" queued %d action plans", len(s.queue))) } -func (s *Scheduler) Restart() { +func (s *Scheduler) restart() { if s.schedulerStarted { s.restartLoop <- true } From 9ba5d4ccae35ff23f65f85d6a1fb72992317379f Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 9 Dec 2015 16:10:29 +0200 Subject: [PATCH 13/22] tests fix --- scheduler/scheduler.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index bbbd34a71..b73117870 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -112,8 +112,10 @@ func (s *Scheduler) Reload(protect bool) { } }() } else { - s.loadActionPlans() - s.restart() + go func() { + s.loadActionPlans() + s.restart() + }() } } From daae3d8cea31ad5a61b9f1e0d6267bf0908be3a4 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 9 Dec 2015 17:03:54 +0200 Subject: [PATCH 14/22] added Categories in AddBalance --- apier/v1/apier.go | 4 +++- general_tests/ddazmbl1_test.go | 2 +- general_tests/ddazmbl2_test.go | 2 +- general_tests/ddazmbl3_test.go | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 2bf808db5..e0df73465 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -112,6 +112,7 @@ type AttrAddBalance struct { Value float64 ExpiryTime string RatingSubject string + Categories string DestinationIds string Weight float64 SharedGroups string @@ -127,7 +128,7 @@ func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error { } tag := utils.ConcatenatedKey(attr.Tenant, attr.Account) if _, err := self.AccountDb.GetAccount(tag); err != nil { - // create user balance if not exists + // create account if not exists account := &engine.Account{ Id: tag, } @@ -158,6 +159,7 @@ func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error { RatingSubject: attr.RatingSubject, Directions: utils.ParseStringMap(attr.Directions), DestinationIds: utils.ParseStringMap(attr.DestinationIds), + Categories: utils.ParseStringMap(attr.Categories), Weight: attr.Weight, SharedGroups: utils.ParseStringMap(attr.SharedGroups), Disabled: attr.Disabled, diff --git a/general_tests/ddazmbl1_test.go b/general_tests/ddazmbl1_test.go index 7155f41da..afc603bb8 100644 --- a/general_tests/ddazmbl1_test.go +++ b/general_tests/ddazmbl1_test.go @@ -130,7 +130,7 @@ TOPUP10_AT,TOPUP10_AC1,ASAP,10` func TestExecuteActions(t *testing.T) { scheduler.NewScheduler(ratingDb).Reload(false) - time.Sleep(time.Second) // Give time to scheduler to topup the account + time.Sleep(10 * time.Millisecond) // Give time to scheduler to topup the account if acnt, err := acntDb.GetAccount("cgrates.org:12344"); err != nil { t.Error(err) } else if len(acnt.BalanceMap) != 2 { diff --git a/general_tests/ddazmbl2_test.go b/general_tests/ddazmbl2_test.go index eab079787..cd371fe7a 100644 --- a/general_tests/ddazmbl2_test.go +++ b/general_tests/ddazmbl2_test.go @@ -129,7 +129,7 @@ TOPUP10_AT,TOPUP10_AC1,ASAP,10` func TestExecuteActions2(t *testing.T) { scheduler.NewScheduler(ratingDb2).Reload(false) - time.Sleep(time.Millisecond) // Give time to scheduler to topup the account + time.Sleep(10 * time.Millisecond) // Give time to scheduler to topup the account if acnt, err := acntDb2.GetAccount("cgrates.org:12345"); err != nil { t.Error(err) } else if len(acnt.BalanceMap) != 2 { diff --git a/general_tests/ddazmbl3_test.go b/general_tests/ddazmbl3_test.go index 0ba5d7a84..2df5c03e3 100644 --- a/general_tests/ddazmbl3_test.go +++ b/general_tests/ddazmbl3_test.go @@ -127,7 +127,7 @@ RP_UK,DR_UK_Mobile_BIG5,ALWAYS,10` func TestExecuteActions3(t *testing.T) { scheduler.NewScheduler(ratingDb3).Reload(false) - time.Sleep(time.Millisecond) // Give time to scheduler to topup the account + time.Sleep(10 * time.Millisecond) // Give time to scheduler to topup the account if acnt, err := acntDb3.GetAccount("cgrates.org:12346"); err != nil { t.Error(err) } else if len(acnt.BalanceMap) != 1 { From 607647eff7bff8a27aba03f0c68608faeb3aba44 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 9 Dec 2015 17:17:24 +0200 Subject: [PATCH 15/22] used ParseTimeDetectLayout in addbalance --- apier/v1/apier.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apier/v1/apier.go b/apier/v1/apier.go index e0df73465..8e7b93739 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -121,7 +121,7 @@ type AttrAddBalance struct { } func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error { - expTime, err := utils.ParseDate(attr.ExpiryTime) + expTime, err := utils.ParseTimeDetectLayout(attr.ExpiryTime, self.Config.DefaultTimezone) if err != nil { *reply = err.Error() return err From ca3ed141bbdc32aca5d841256942f4c95f96cf8c Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 9 Dec 2015 17:44:58 +0200 Subject: [PATCH 16/22] bettter Overwritte for SetRatingProfile --- apier/v1/apier.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 8e7b93739..1c3c4033d 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -597,9 +597,17 @@ func (self *ApierV1) SetRatingProfile(attrs AttrSetRatingProfile, reply *string) return utils.ErrExists } } - rpfl := &engine.RatingProfile{Id: keyId, RatingPlanActivations: make(engine.RatingPlanActivations, len(attrs.RatingPlanActivations))} - for idx, ra := range attrs.RatingPlanActivations { - at, err := utils.ParseDate(ra.ActivationTime) + var rpfl *engine.RatingProfile + if attrs.Overwrite { + rpfl = &engine.RatingProfile{Id: keyId, RatingPlanActivations: make(engine.RatingPlanActivations, 0)} + } else { + var err error + if rpfl, err = self.RatingDb.GetRatingProfile(keyId, false); err != nil { + return utils.NewErrServerError(err) + } + } + for _, ra := range attrs.RatingPlanActivations { + at, err := utils.ParseTimeDetectLayout(ra.ActivationTime, self.Config.DefaultTimezone) if err != nil { return fmt.Errorf(fmt.Sprintf("%s:Cannot parse activation time from %v", utils.ErrServerError.Error(), ra.ActivationTime)) } @@ -608,8 +616,8 @@ func (self *ApierV1) SetRatingProfile(attrs AttrSetRatingProfile, reply *string) } else if !exists { return fmt.Errorf(fmt.Sprintf("%s:RatingPlanId:%s", utils.ErrNotFound.Error(), ra.RatingPlanId)) } - rpfl.RatingPlanActivations[idx] = &engine.RatingPlanActivation{ActivationTime: at, RatingPlanId: ra.RatingPlanId, - FallbackKeys: utils.FallbackSubjKeys(tpRpf.Direction, tpRpf.Tenant, tpRpf.Category, ra.FallbackSubjects)} + rpfl.RatingPlanActivations = append(rpfl.RatingPlanActivations, &engine.RatingPlanActivation{ActivationTime: at, RatingPlanId: ra.RatingPlanId, + FallbackKeys: utils.FallbackSubjKeys(tpRpf.Direction, tpRpf.Tenant, tpRpf.Category, ra.FallbackSubjects)}) } if err := self.RatingDb.SetRatingProfile(rpfl); err != nil { return utils.NewErrServerError(err) From 8caced7fb4bcab6614ee6caf514df58a5deba936 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 9 Dec 2015 17:55:19 +0200 Subject: [PATCH 17/22] fix for Overwrite flag --- apier/v1/apier.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 1c3c4033d..7718bda1e 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -599,12 +599,10 @@ func (self *ApierV1) SetRatingProfile(attrs AttrSetRatingProfile, reply *string) } var rpfl *engine.RatingProfile if attrs.Overwrite { + rpfl, err = self.RatingDb.GetRatingProfile(keyId, false) + } + if rpfl == nil { rpfl = &engine.RatingProfile{Id: keyId, RatingPlanActivations: make(engine.RatingPlanActivations, 0)} - } else { - var err error - if rpfl, err = self.RatingDb.GetRatingProfile(keyId, false); err != nil { - return utils.NewErrServerError(err) - } } for _, ra := range attrs.RatingPlanActivations { at, err := utils.ParseTimeDetectLayout(ra.ActivationTime, self.Config.DefaultTimezone) From 2bdb583a98563aa701520dea354130a7a1a00477 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 9 Dec 2015 17:56:16 +0200 Subject: [PATCH 18/22] compilation fix :( --- apier/v1/apier.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 7718bda1e..166477e33 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -599,7 +599,7 @@ func (self *ApierV1) SetRatingProfile(attrs AttrSetRatingProfile, reply *string) } var rpfl *engine.RatingProfile if attrs.Overwrite { - rpfl, err = self.RatingDb.GetRatingProfile(keyId, false) + rpfl, _ = self.RatingDb.GetRatingProfile(keyId, false) } if rpfl == nil { rpfl = &engine.RatingProfile{Id: keyId, RatingPlanActivations: make(engine.RatingPlanActivations, 0)} From 3397691935096fb9fd87ce8cbebf8f632f69a258 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 9 Dec 2015 18:10:46 +0200 Subject: [PATCH 19/22] smaller scheduler loop detection and local tests fix --- general_tests/tutorial_local_test.go | 3 ++- scheduler/scheduler.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/general_tests/tutorial_local_test.go b/general_tests/tutorial_local_test.go index 5cbf192df..004fd5890 100644 --- a/general_tests/tutorial_local_test.go +++ b/general_tests/tutorial_local_test.go @@ -104,7 +104,7 @@ func TestTutLocalLoadTariffPlanFromFolder(t *testing.T) { } else if loadInst.LoadId == "" { t.Error("Empty loadId received, loadInstance: ", loadInst) } - time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups + time.Sleep(100*time.Millisecond + time.Duration(*waitRater)*time.Millisecond) // Give time for scheduler to execute topups } // Check loaded stats @@ -1098,6 +1098,7 @@ func TestTutLocalSetAccount(t *testing.T) { Offset int // Set the item offset Limit int // Limit number of items retrieved } + time.Sleep(100*time.Millisecond + time.Duration(*waitRater)*time.Millisecond) // Give time for scheduler to execute topups var acnts []*engine.Account if err := tutLocalRpc.Call("ApierV2.GetAccounts", utils.AttrGetAccounts{Tenant: attrs.Tenant, AccountIds: []string{attrs.Account}}, &acnts); err != nil { t.Error(err) diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index b73117870..5b29216ec 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -100,7 +100,7 @@ func (s *Scheduler) Reload(protect bool) { } s.waitingReload = true go func() { - t := time.NewTicker(time.Second) // wait 1 second before start + t := time.NewTicker(100 * time.Millisecond) // wait for loops before start select { case <-s.loopChecker: t.Stop() // cancel reload From 100a15644e504dbff58290e2d3b53d9cf4e22ec9 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 9 Dec 2015 18:27:20 +0200 Subject: [PATCH 20/22] apier local test fix --- apier/v1/apier_local_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index 10ed9ec9d..aee3f3cc3 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -1133,6 +1133,7 @@ func TestApierGetAccount(t *testing.T) { if !*testLocal { return } + time.Sleep(100 * time.Millisecond) // give scheduler time to react var reply *engine.Account attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} if err := rater.Call("ApierV2.GetAccount", attrs, &reply); err != nil { From d13486ceac030130d61d2ef6848da7fa4b0cee27 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 9 Dec 2015 19:12:29 +0200 Subject: [PATCH 21/22] update ratingprofile on non overwrite --- apier/v1/apier.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 166477e33..a4d041b0c 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -590,17 +590,17 @@ func (self *ApierV1) SetRatingProfile(attrs AttrSetRatingProfile, reply *string) } tpRpf := utils.TPRatingProfile{Tenant: attrs.Tenant, Category: attrs.Category, Direction: attrs.Direction, Subject: attrs.Subject} keyId := tpRpf.KeyId() + var rpfl *engine.RatingProfile if !attrs.Overwrite { if exists, err := self.RatingDb.HasData(utils.RATING_PROFILE_PREFIX, keyId); err != nil { return utils.NewErrServerError(err) } else if exists { - return utils.ErrExists + var err error + if rpfl, err = self.RatingDb.GetRatingProfile(keyId, false); err != nil { + return utils.NewErrServerError(err) + } } } - var rpfl *engine.RatingProfile - if attrs.Overwrite { - rpfl, _ = self.RatingDb.GetRatingProfile(keyId, false) - } if rpfl == nil { rpfl = &engine.RatingProfile{Id: keyId, RatingPlanActivations: make(engine.RatingPlanActivations, 0)} } From d493952d541cc0fe11aa2cad18ea6a8760a2f0c5 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 9 Dec 2015 19:21:54 +0200 Subject: [PATCH 22/22] fix local test --- apier/v1/apier_local_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index aee3f3cc3..404f77228 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -735,8 +735,8 @@ func TestApierSetRatingProfile(t *testing.T) { } else if reply != "OK" { t.Error("Calling ApierV1.SetRatingProfile got reply: ", reply) } - // Calling the second time should raise EXISTS - if err := rater.Call("ApierV1.SetRatingProfile", rpf, &reply); err == nil || err.Error() != "EXISTS" { + // Calling the second time should not raise EXISTS + if err := rater.Call("ApierV1.SetRatingProfile", rpf, &reply); err != nil { t.Error("Unexpected result on duplication: ", err.Error()) } time.Sleep(10 * time.Millisecond) // Give time for cache reload