diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index 6ecdf14f4..6f15d29c3 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -71,6 +71,7 @@ type AttrRemActionTiming struct { Tenant string // Tenant he account belongs to Account string // Account name Direction string // Traffic direction + ReloadScheduler bool // If set it will reload the scheduler after adding } // Removes an ActionTimings or parts of it depending on filters being set @@ -83,18 +84,26 @@ func (self *ApierV1) RemActionTiming(attrs AttrRemActionTiming, reply *string) e return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } } - // ToDo: lock here actionTimings - ats, err := self.AccountDb.GetActionTimings(attrs.ActionTimingsId) + _, err := engine.AccLock.Guard(engine.ACTION_TIMING_PREFIX+attrs.ActionTimingId, func() (float64, error) { // ToDo: Expand the scheduler to consider the locks also + ats, err := self.AccountDb.GetActionTimings(attrs.ActionTimingsId) + if err != nil { + return 0, err + } else if len(ats) == 0 { + return 0, errors.New(utils.ERR_NOT_FOUND) + } + ats = engine.RemActionTiming(ats, attrs.ActionTimingId, utils.BalanceKey(attrs.Tenant, attrs.Account, attrs.Direction)) + if err := self.AccountDb.SetActionTimings(attrs.ActionTimingsId, ats); err != nil { + return 0, err + } + return 0, nil + }) if err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) - } else if len(ats) == 0 { - return errors.New(utils.ERR_NOT_FOUND) } - ats = engine.RemActionTiming(ats, attrs.ActionTimingId, utils.BalanceKey(attrs.Tenant, attrs.Account, attrs.Direction)) - if err := self.AccountDb.SetActionTimings(attrs.ActionTimingsId, ats); err != nil { - return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) + if attrs.ReloadScheduler && self.Sched != nil { + self.Sched.LoadActionTimings(self.AccountDb) + self.Sched.Restart() } - // ToDo: Unlock here actionTimings *reply = OK return nil } @@ -120,27 +129,34 @@ type AttrRemAcntActionTriggers struct { } // Returns a list of ActionTriggers on an account -func (self *ApierV1) RemAccountActionTriggers(attrs AttrRemAcntActionTrigger, reply *string) error { +func (self *ApierV1) RemAccountActionTriggers(attrs AttrRemAcntActionTriggers, reply *string) error { if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "Account", "Direction"}); len(missing) != 0 { return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } - ub, err := self.AccountDb.GetUserBalance(utils.BalanceKey(attrs.Tenant, attrs.Account, attrs.Direction)) + balanceId := utils.BalanceKey(attrs.Tenant, attrs.Account, attrs.Direction) + _, err := engine.AccLock.Guard(balanceId, func() (float64, error) { + ub, err := self.AccountDb.GetUserBalance(balanceId) + if err != nil { + return 0, err + } + for idx, actr := range ub.ActionTriggers { + if len(attrs.ActionTriggerId) != 0 && actr.Id != attrs.ActionTriggerId { // Empty actionTriggerId will match always + continue + } + if len(ub.ActionTriggers) != 1 { // Remove by index + ub.ActionTriggers[idx], ub.ActionTriggers = ub.ActionTriggers[len(ub.ActionTriggers)-1], ub.ActionTriggers[:len(ub.ActionTriggers)-1] + } else { // For last item, simply reinit the slice + ub.ActionTriggers = make(engine.ActionTriggerPriotityList, 0) + } + } + if err := self.AccountDb.SetUserBalance(ub); err != nil { + return 0, err + } + return 0, nil + }) if err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } - for idx, actr := range ub.ActionTriggers { - if len(attrs.ActionTriggerId) != 0 && actr.Id != attrs.ActionTriggerId { // Empty actionTriggerId will match always - continue - } - if len(ub.ActionTriggers) != 1 { // Remove by index - ub.ActionTriggers[idx], ub.ActionTriggers = ub.ActionTriggers[len(ub.ActionTriggers)-1], ub.ActionTriggers[:len(ub.ActionTriggers)-1] - } else { // For last item, simply reinit the slice - ub.ActionTriggers = make(engine.ActionTriggerPriotityList, 0) - } - } - if err := self.AccountDb.SetUserBalance(ub); err != nil { - return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) - } *reply = OK return nil } diff --git a/apier/v1/apier.go b/apier/v1/apier.go index dc0790e94..70673cc57 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -288,6 +288,7 @@ type AttrSetActionTimings struct { ActionTimingsId string // Profile id Overwrite bool // If previously defined, will be overwritten ActionTimings []*ApiActionTiming // Set of actions this Actions profile will perform + ReloadScheduler bool // Enables automatic reload of the scheduler (eg: useful when adding a single action timing) } type ApiActionTiming struct { @@ -342,6 +343,10 @@ func (self *ApierV1) SetActionTimings(attrs AttrSetActionTimings, reply *string) if err := self.AccountDb.SetActionTimings(attrs.ActionTimingsId, storeAtms); err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } + if attrs.ReloadScheduler && self.Sched != nil { + self.Sched.LoadActionTimings(self.AccountDb) + self.Sched.Restart() + } *reply = OK return nil } @@ -375,7 +380,7 @@ func (self *ApierV1) AddTriggeredAction(attr AttrAddActionTrigger, reply *string Executed: false, } - tag := fmt.Sprintf("%s:%s:%s", attr.Direction, attr.Tenant, attr.Account) + tag := utils.BalanceKey(attr.Tenant, attr.Account, attr.Direction) _, err := engine.AccLock.Guard(tag, func() (float64, error) { userBalance, err := self.AccountDb.GetUserBalance(tag) if err != nil { @@ -410,7 +415,7 @@ func (self *ApierV1) AddAccount(attr AttrAddAccount, reply *string) error { if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Direction", "Account", "Type", "ActionTimingsId"}); len(missing) != 0 { return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } - tag := fmt.Sprintf("%s:%s:%s", attr.Direction, attr.Tenant, attr.Account) + tag := utils.BalanceKey(attr.Tenant, attr.Account, attr.Direction) ub := &engine.UserBalance{ Id: tag, Type: attr.Type, @@ -441,7 +446,7 @@ func (self *ApierV1) AddAccount(attr AttrAddAccount, reply *string) error { } // Process dependencies and load a specific AccountActions profile from storDb into dataDb. -func (self *ApierV1) SetAccountActions(attrs utils.TPAccountActions, reply *string) error { +func (self *ApierV1) LoadAccountActions(attrs utils.TPAccountActions, reply *string) error { if missing := utils.MissingStructFields(&attrs, []string{"TPid", "LoadId", "Tenant", "Account", "Direction"}); len(missing) != 0 { return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } @@ -464,14 +469,14 @@ func (self *ApierV1) SetAccountActions(attrs utils.TPAccountActions, reply *stri } func (self *ApierV1) ReloadScheduler(input string, reply *string) error { - if self.Sched != nil { - self.Sched.LoadActionTimings(self.AccountDb) - self.Sched.Restart() - *reply = OK - return nil + if self.Sched == nil { + return errors.New(utils.ERR_NOT_FOUND) } - *reply = utils.ERR_NOT_FOUND - return errors.New(utils.ERR_NOT_FOUND) + self.Sched.LoadActionTimings(self.AccountDb) + self.Sched.Restart() + *reply = OK + return nil + } func (self *ApierV1) ReloadCache(attrs utils.ApiReloadCache, reply *string) error { diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index 3074760c7..9c544421e 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -754,17 +754,17 @@ func TestApierSetRatingProfile(t *testing.T) { } } -// Test here SetAccountActions -func TestApierSetAccountActions(t *testing.T) { +// Test here LoadAccountActions +func TestApierLoadAccountActions(t *testing.T) { if !*testLocal { return } reply := "" aa1 := &utils.TPAccountActions{TPid: engine.TEST_SQL, LoadId: engine.TEST_SQL, Tenant: "cgrates.org", Account: "1001", Direction: "*out"} - if err := rater.Call("ApierV1.SetAccountActions", aa1, &reply); err != nil { - t.Error("Got error on ApierV1.SetAccountActions: ", err.Error()) + if err := rater.Call("ApierV1.LoadAccountActions", aa1, &reply); err != nil { + t.Error("Got error on ApierV1.LoadAccountActions: ", err.Error()) } else if reply != "OK" { - t.Error("Calling ApierV1.SetAccountActions got reply: ", reply) + t.Error("Calling ApierV1.LoadAccountActions got reply: ", reply) } }