コード例 #1
0
ファイル: loader_db.go プロジェクト: intralanman/cgrates
func (dbr *DbReader) LoadActions() (err error) {
	storActs, err := dbr.storDb.GetTpActions(dbr.tpid, "")
	if err != nil {
		return err
	}
	// map[string][]*Action
	for tag, tpacts := range storActs {
		acts := make([]*Action, len(tpacts))
		for idx, tpact := range tpacts {
			acts[idx] = &Action{
				Id:               utils.GenUUID(),
				ActionType:       tpact.Identifier,
				BalanceType:      tpact.BalanceType,
				Direction:        tpact.Direction,
				Weight:           tpact.Weight,
				ExtraParameters:  tpact.ExtraParameters,
				ExpirationString: tpact.ExpiryTime,
				Balance: &Balance{
					Uuid:          utils.GenUUID(),
					Value:         tpact.Units,
					Weight:        tpact.BalanceWeight,
					RatingSubject: tpact.RatingSubject,
					Category:      tpact.Category,
					DestinationId: tpact.DestinationId,
				},
			}
		}
		dbr.actions[tag] = acts
	}
	return nil
}
コード例 #2
0
ファイル: loader_csv.go プロジェクト: intralanman/cgrates
func (csvr *CSVReader) LoadActions() (err error) {
	csvReader, fp, err := csvr.readerFunc(csvr.actionsFn, csvr.sep, utils.ACTIONS_NRCOLS)
	if err != nil {
		log.Print("Could not load action file: ", err)
		// allow writing of the other values
		return nil
	}
	if fp != nil {
		defer fp.Close()
	}
	for record, err := csvReader.Read(); err == nil; record, err = csvReader.Read() {
		tag := record[0]
		var units float64
		if len(record[4]) == 0 { // Not defined
			units = 0.0
		} else {
			units, err = strconv.ParseFloat(record[4], 64)
			if err != nil {
				return fmt.Errorf("Could not parse action units: %v", err)
			}
		}
		var balanceWeight float64
		if len(record[9]) == 0 { // Not defined
			balanceWeight = 0.0
		} else {
			balanceWeight, err = strconv.ParseFloat(record[9], 64)
			if err != nil {
				return fmt.Errorf("Could not parse action balance weight: %v", err)
			}
		}
		weight, err := strconv.ParseFloat(record[12], 64)
		if err != nil {
			return fmt.Errorf("Could not parse action weight: %v", err)
		}
		a := &Action{
			Id:               utils.GenUUID(),
			ActionType:       record[1],
			BalanceType:      record[2],
			Direction:        record[3],
			Weight:           weight,
			ExpirationString: record[5],
			ExtraParameters:  record[11],
			Balance: &Balance{
				Uuid:          utils.GenUUID(),
				Value:         units,
				Weight:        balanceWeight,
				DestinationId: record[6],
				RatingSubject: record[7],
				Category:      record[8],
				SharedGroup:   record[10],
			},
		}
		if _, err := utils.ParseDate(a.ExpirationString); err != nil {
			return fmt.Errorf("Could not parse expiration time: %v", err)
		}
		csvr.actions[tag] = append(csvr.actions[tag], a)
	}
	return
}
コード例 #3
0
ファイル: onstor_it_test.go プロジェクト: cgrates/cgrates
func testOnStorITCacheActions(t *testing.T) {
	acts := Actions{
		&Action{
			Id:               "MINI",
			ActionType:       TOPUP_RESET,
			ExpirationString: UNLIMITED,
			Weight:           10,
			Balance: &BalanceFilter{
				Type:       utils.StringPointer(utils.MONETARY),
				Uuid:       utils.StringPointer(utils.GenUUID()),
				Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)),
				Value: &utils.ValueFormula{Static: 10,
					Params: make(map[string]interface{})},
				Weight:   utils.Float64Pointer(10),
				Disabled: utils.BoolPointer(false),
				Timings:  make([]*RITiming, 0),
				Blocker:  utils.BoolPointer(false),
			},
		},
		&Action{
			Id:               "MINI",
			ActionType:       TOPUP,
			ExpirationString: UNLIMITED,
			Weight:           10,
			Balance: &BalanceFilter{
				Type:       utils.StringPointer(utils.VOICE),
				Uuid:       utils.StringPointer(utils.GenUUID()),
				Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)),
				Value: &utils.ValueFormula{Static: 100,
					Params: make(map[string]interface{})},
				Weight:         utils.Float64Pointer(10),
				RatingSubject:  utils.StringPointer("test"),
				DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")),
				Disabled:       utils.BoolPointer(false),
				Timings:        make([]*RITiming, 0),
				Blocker:        utils.BoolPointer(false),
			},
		},
	}
	if err := onStor.SetActions(acts[0].Id, acts, utils.NonTransactional); err != nil {
		t.Error(err)
	}
	if _, hasIt := cache.Get(utils.ACTION_PREFIX + acts[0].Id); hasIt {
		t.Error("Already in cache")
	}
	if err := onStor.CacheDataFromDB(utils.ACTION_PREFIX, []string{acts[0].Id}, false); err != nil {
		t.Error(err)
	}
	if itm, hasIt := cache.Get(utils.ACTION_PREFIX + acts[0].Id); !hasIt {
		t.Error("Did not cache")
	} else if rcv := itm.(Actions); !reflect.DeepEqual(acts, rcv) {
		t.Errorf("Expecting: %+v, received: %+v", acts, rcv)
	}
}
コード例 #4
0
ファイル: onstor_it_test.go プロジェクト: cgrates/cgrates
func testOnStorITCacheActionPlan(t *testing.T) {
	ap := &ActionPlan{
		Id:         "MORE_MINUTES",
		AccountIDs: utils.StringMap{"vdf:minitsboy": true},
		ActionTimings: []*ActionTiming{
			&ActionTiming{
				Uuid: utils.GenUUID(),
				Timing: &RateInterval{
					Timing: &RITiming{
						Years:     utils.Years{2012},
						Months:    utils.Months{},
						MonthDays: utils.MonthDays{},
						WeekDays:  utils.WeekDays{},
						StartTime: utils.ASAP,
					},
				},
				Weight:    10,
				ActionsID: "MINI",
			},
			&ActionTiming{
				Uuid: utils.GenUUID(),
				Timing: &RateInterval{
					Timing: &RITiming{
						Years:     utils.Years{2012},
						Months:    utils.Months{},
						MonthDays: utils.MonthDays{},
						WeekDays:  utils.WeekDays{},
						StartTime: utils.ASAP,
					},
				},
				Weight:    10,
				ActionsID: "SHARED",
			},
		},
	}
	if err := onStor.SetActionPlan(ap.Id, ap, true, utils.NonTransactional); err != nil {
		t.Error(err)
	}
	if _, hasIt := cache.Get(utils.ACTION_PLAN_PREFIX + ap.Id); hasIt {
		t.Error("Already in cache")
	}
	if err := onStor.CacheDataFromDB(utils.ACTION_PLAN_PREFIX, []string{ap.Id}, false); err != nil {
		t.Error(err)
	}
	if itm, hasIt := cache.Get(utils.ACTION_PLAN_PREFIX + ap.Id); !hasIt {
		t.Error("Did not cache")
	} else if rcv := itm.(*ActionPlan); !reflect.DeepEqual(ap, rcv) {
		t.Errorf("Expecting: %+v, received: %+v", ap, rcv)
	}
}
コード例 #5
0
ファイル: apier.go プロジェクト: bhepp/cgrates
func (self *ApierV1) SetActions(attrs utils.AttrSetActions, reply *string) 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 != "" { // Add some inter-dependent parameters - if balanceType then we are not talking about simply calling actions
			requiredFields = append(requiredFields, "Direction", "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 := self.RatingDb.HasData(utils.ACTION_PREFIX, 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 {
		a := &engine.Action{
			Id:               utils.GenUUID(),
			ActionType:       apiAct.Identifier,
			BalanceType:      apiAct.BalanceType,
			Weight:           apiAct.Weight,
			ExpirationString: apiAct.ExpiryTime,
			ExtraParameters:  apiAct.ExtraParameters,
			Filter:           apiAct.Filter,
			Balance: &engine.Balance{
				Uuid:           utils.GenUUID(),
				Id:             apiAct.BalanceId,
				Value:          apiAct.Units,
				Weight:         apiAct.BalanceWeight,
				Directions:     utils.ParseStringMap(apiAct.Directions),
				DestinationIds: utils.ParseStringMap(apiAct.DestinationIds),
				RatingSubject:  apiAct.RatingSubject,
				SharedGroups:   utils.ParseStringMap(apiAct.SharedGroups),
			},
		}
		storeActions[idx] = a
	}
	if err := self.RatingDb.SetActions(attrs.ActionsId, storeActions); err != nil {
		return utils.NewErrServerError(err)
	}
	self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PREFIX: []string{utils.ACTION_PREFIX + attrs.ActionsId}})
	*reply = OK
	return nil
}
コード例 #6
0
ファイル: tp_reader.go プロジェクト: gale320/cgrates
func (tpr *TpReader) LoadActionTriggers() (err error) {
	tps, err := tpr.lr.GetTpActionTriggers(tpr.tpid, "")
	if err != nil {
		return err
	}
	storAts, err := TpActionTriggers(tps).GetActionTriggers()
	if err != nil {
		return err
	}
	for key, atrsLst := range storAts {
		atrs := make([]*ActionTrigger, len(atrsLst))
		for idx, atr := range atrsLst {
			balanceExpirationDate, _ := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone)
			id := atr.Id
			if id == "" {
				id = utils.GenUUID()
			}
			minSleep, err := utils.ParseDurationWithSecs(atr.MinSleep)
			if err != nil {
				return err
			}
			atrs[idx] = &ActionTrigger{
				Id:                    id,
				ThresholdType:         atr.ThresholdType,
				ThresholdValue:        atr.ThresholdValue,
				Recurrent:             atr.Recurrent,
				MinSleep:              minSleep,
				BalanceId:             atr.BalanceId,
				BalanceType:           atr.BalanceType,
				BalanceDirection:      atr.BalanceDirection,
				BalanceDestinationIds: atr.BalanceDestinationIds,
				BalanceWeight:         atr.BalanceWeight,
				BalanceExpirationDate: balanceExpirationDate,
				BalanceTimingTags:     atr.BalanceTimingTags,
				BalanceRatingSubject:  atr.BalanceRatingSubject,
				BalanceCategory:       atr.BalanceCategory,
				BalanceSharedGroup:    atr.BalanceSharedGroup,
				Weight:                atr.Weight,
				ActionsId:             atr.ActionsId,
				MinQueuedItems:        atr.MinQueuedItems,
			}
			if atrs[idx].Id == "" {
				atrs[idx].Id = utils.GenUUID()
			}
		}
		tpr.actionsTriggers[key] = atrs
	}

	return nil
}
コード例 #7
0
ファイル: apier.go プロジェクト: intralanman/cgrates
func (self *ApierV1) SetActions(attrs AttrSetActions, reply *string) error {
	if missing := utils.MissingStructFields(&attrs, []string{"ActionsId", "Actions"}); len(missing) != 0 {
		return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing)
	}
	for _, action := range attrs.Actions {
		requiredFields := []string{"Identifier", "Weight"}
		if action.BalanceType != "" { // Add some inter-dependent parameters - if balanceType then we are not talking about simply calling actions
			requiredFields = append(requiredFields, "Direction", "Units")
		}
		if missing := utils.MissingStructFields(action, requiredFields); len(missing) != 0 {
			return fmt.Errorf("%s:Action:%s:%v", utils.ERR_MANDATORY_IE_MISSING, action.Identifier, missing)
		}
	}
	if !attrs.Overwrite {
		if exists, err := self.AccountDb.HasData(engine.ACTION_PREFIX, attrs.ActionsId); err != nil {
			return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
		} else if exists {
			return errors.New(utils.ERR_EXISTS)
		}
	}
	storeActions := make(engine.Actions, len(attrs.Actions))
	for idx, apiAct := range attrs.Actions {
		a := &engine.Action{
			Id:               utils.GenUUID(),
			ActionType:       apiAct.Identifier,
			BalanceType:      apiAct.BalanceType,
			Direction:        apiAct.Direction,
			Weight:           apiAct.Weight,
			ExpirationString: apiAct.ExpiryTime,
			ExtraParameters:  apiAct.ExtraParameters,
			Balance: &engine.Balance{
				Uuid:          utils.GenUUID(),
				Value:         apiAct.Units,
				Weight:        apiAct.BalanceWeight,
				DestinationId: apiAct.DestinationId,
				RatingSubject: apiAct.RatingSubject,
				SharedGroup:   apiAct.SharedGroup,
			},
		}
		storeActions[idx] = a
	}
	if err := self.AccountDb.SetActions(attrs.ActionsId, storeActions); err != nil {
		return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
	}
	didNotChange := []string{}
	self.AccountDb.CacheAccounting(nil, didNotChange, didNotChange, didNotChange)
	*reply = OK
	return nil
}
コード例 #8
0
ファイル: storage_map.go プロジェクト: iwada/cgrates
func (ms *MapStorage) cacheAccounting(loadID string, alsKeys []string) error {
	CacheBeginTransaction()
	if alsKeys == nil {
		CacheRemPrefixKey(utils.ALIASES_PREFIX) // Forced until we can fine tune it
	}
	for k, _ := range ms.dict {
		if strings.HasPrefix(k, utils.ALIASES_PREFIX) {
			// check if it already exists
			// to remove reverse cache keys
			if avs, err := CacheGet(k); err == nil && avs != nil {
				al := &Alias{Values: avs.(AliasValues)}
				al.SetId(k[len(utils.ALIASES_PREFIX):])
				al.RemoveReverseCache()
			}
			CacheRemKey(k)
			if _, err := ms.GetAlias(k[len(utils.ALIASES_PREFIX):], true); err != nil {
				CacheRollbackTransaction()
				return err
			}
		}
	}
	CacheCommitTransaction()

	loadHistList, err := ms.GetLoadHistory(1, true)
	if err != nil || len(loadHistList) == 0 {
		utils.Logger.Info(fmt.Sprintf("could not get load history: %v (%v)", loadHistList, err))
	}
	var loadHist *utils.LoadInstance
	if len(loadHistList) == 0 {
		loadHist = &utils.LoadInstance{
			RatingLoadID:     utils.GenUUID(),
			AccountingLoadID: utils.GenUUID(),
			LoadID:           loadID,
			LoadTime:         time.Now(),
		}
	} else {
		loadHist = loadHistList[0]
		loadHist.AccountingLoadID = utils.GenUUID()
		loadHist.LoadID = loadID
		loadHist.LoadTime = time.Now()
	}
	if err := ms.AddLoadHistory(loadHist, 10); err != nil {
		utils.Logger.Info(fmt.Sprintf("error saving load history: %v (%v)", loadHist, err))
		return err
	}
	ms.GetLoadHistory(1, true) // to load last instance in cache
	return utils.SaveCacheFileInfo(ms.cacheDumpDir, &utils.CacheFileInfo{Encoding: utils.MSGPACK, LoadInfo: loadHist})
}
コード例 #9
0
ファイル: smg_externalconns.go プロジェクト: eloycoto/cgrates
// Index the client connection so we can use it to communicate back
func (self *SMGExternalConnections) OnClientConnect(clnt *rpc2.Client) {
	self.connMux.Lock()
	defer self.connMux.Unlock()
	connId := utils.GenUUID()
	clnt.State.Set(CGR_CONNUUID, connId) // Set unique id for the connection so we can identify it later in requests
	self.conns[connId] = clnt
}
コード例 #10
0
ファイル: cache.go プロジェクト: cgrates/cgrates
func BeginTransaction() string {
	transID := utils.GenUUID()
	transBufMux.Lock()
	transactionBuffer[transID] = make([]*transactionItem, 0)
	transBufMux.Unlock()
	return transID
}
コード例 #11
0
ファイル: loader_db.go プロジェクト: intralanman/cgrates
func (dbr *DbReader) LoadActionTriggers() (err error) {
	atrsMap, err := dbr.storDb.GetTpActionTriggers(dbr.tpid, "")
	if err != nil {
		return err
	}
	for key, atrsLst := range atrsMap {
		atrs := make([]*ActionTrigger, len(atrsLst))
		for idx, apiAtr := range atrsLst {
			balance_expiration_date, _ := utils.ParseTimeDetectLayout(apiAtr.BalanceExpirationDate)
			atrs[idx] = &ActionTrigger{
				Id:                    utils.GenUUID(),
				BalanceType:           apiAtr.BalanceType,
				Direction:             apiAtr.Direction,
				ThresholdType:         apiAtr.ThresholdType,
				ThresholdValue:        apiAtr.ThresholdValue,
				Recurrent:             apiAtr.Recurrent,
				MinSleep:              apiAtr.MinSleep,
				DestinationId:         apiAtr.DestinationId,
				BalanceWeight:         apiAtr.BalanceWeight,
				BalanceExpirationDate: balance_expiration_date,
				BalanceRatingSubject:  apiAtr.BalanceRatingSubject,
				BalanceCategory:       apiAtr.BalanceCategory,
				BalanceSharedGroup:    apiAtr.BalanceSharedGroup,
				Weight:                apiAtr.Weight,
				ActionsId:             apiAtr.ActionsId,
				MinQueuedItems:        apiAtr.MinQueuedItems,
			}
		}
		dbr.actionsTriggers[key] = atrs
	}
	return err
}
コード例 #12
0
ファイル: config.go プロジェクト: iwada/cgrates
func NewDefaultCGRConfig() (*CGRConfig, error) {
	cfg := new(CGRConfig)
	cfg.InstanceID = utils.GenUUID()
	cfg.DataFolderPath = "/usr/share/cgrates/"
	cfg.SmGenericConfig = new(SmGenericConfig)
	cfg.SmFsConfig = new(SmFsConfig)
	cfg.SmKamConfig = new(SmKamConfig)
	cfg.SmOsipsConfig = new(SmOsipsConfig)
	cfg.diameterAgentCfg = new(DiameterAgentCfg)
	cfg.ConfigReloads = make(map[string]chan struct{})
	cfg.ConfigReloads[utils.CDRC] = make(chan struct{}, 1)
	cfg.ConfigReloads[utils.CDRC] <- struct{}{} // Unlock the channel
	cfg.ConfigReloads[utils.CDRE] = make(chan struct{}, 1)
	cfg.ConfigReloads[utils.CDRE] <- struct{}{} // Unlock the channel
	cfg.ConfigReloads[utils.SURETAX] = make(chan struct{}, 1)
	cfg.ConfigReloads[utils.SURETAX] <- struct{}{} // Unlock the channel
	cfg.ConfigReloads[utils.DIAMETER_AGENT] = make(chan struct{}, 1)
	cfg.ConfigReloads[utils.DIAMETER_AGENT] <- struct{}{} // Unlock the channel
	cgrJsonCfg, err := NewCgrJsonCfgFromReader(strings.NewReader(CGRATES_CFG_JSON))
	if err != nil {
		return nil, err
	}
	cfg.MaxCallDuration = time.Duration(3) * time.Hour // Hardcoded for now
	if err := cfg.loadFromJsonCfg(cgrJsonCfg); err != nil {
		return nil, err
	}
	cfg.dfltCdreProfile = cfg.CdreProfiles[utils.META_DEFAULT].Clone() // So default will stay unique, will have nil pointer in case of no defaults loaded which is an extra check
	cfg.dfltCdrcProfile = cfg.CdrcProfiles["/var/spool/cgrates/cdrc/in"][0].Clone()
	dfltFsConnConfig = cfg.SmFsConfig.EventSocketConns[0] // We leave it crashing here on purpose if no Connection defaults defined
	dfltKamConnConfig = cfg.SmKamConfig.EvapiConns[0]
	if err := cfg.checkConfigSanity(); err != nil {
		return nil, err
	}
	return cfg, nil
}
コード例 #13
0
ファイル: libconfig.go プロジェクト: foehn/cgrates
func (rplCfg CdrReplicationCfg) GetFallbackFileName() string {
	serverName := url.QueryEscape(rplCfg.Server)

	result := fmt.Sprintf("cdr_%s_%s_%s.form",
		rplCfg.Transport,
		serverName, utils.GenUUID())
	return result
}
コード例 #14
0
ファイル: account.go プロジェクト: henrylee2cn/cgrates
// 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 (ub *Account) debitBalanceAction(a *Action, reset bool) error {
	if a == nil {
		return errors.New("nil action")
	}
	bClone := a.Balance.Clone()

	if ub.BalanceMap == nil {
		ub.BalanceMap = make(map[string]BalanceChain, 1)
	}
	found := false
	if a.Direction == "" {
		a.Direction = utils.OUT
	}
	id := a.BalanceType + a.Direction
	ub.CleanExpiredBalances()
	for _, b := range ub.BalanceMap[id] {
		if b.IsExpired() {
			continue // just to be safe (cleaned expired balances above)
		}
		b.account = ub
		if b.MatchFilter(a.Balance) {
			if reset {
				b.SetValue(0)
			}
			b.SubstractValue(bClone.GetValue())
			found = true
		}
	}
	// if it is not found then we add it to the list
	if !found {
		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
		if bClone.Uuid == "" {
			bClone.Uuid = utils.GenUUID()
		}
		ub.BalanceMap[id] = append(ub.BalanceMap[id], bClone)
	}
	if a.Balance.SharedGroup != "" {
		// add shared group member
		sg, err := ratingStorage.GetSharedGroup(a.Balance.SharedGroup, false)
		if err != nil || sg == nil {
			//than problem
			utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", a.Balance.SharedGroup))
		} else {
			if !utils.IsSliceMember(sg.MemberIds, ub.Id) {
				// add member and save
				sg.MemberIds = append(sg.MemberIds, ub.Id)
				ratingStorage.SetSharedGroup(sg)
			}
		}
	}
	ub.executeActionTriggers(nil)
	return nil //ub.BalanceMap[id].GetTotalValue()
}
コード例 #15
0
ファイル: action_trigger.go プロジェクト: henrylee2cn/cgrates
// clone with new id(uuid)
func (atrs ActionTriggers) Clone() ActionTriggers {
	// set ids to action triggers
	var newATriggers ActionTriggers
	for _, atr := range atrs {
		newAtr := atr.Clone()
		newAtr.Id = utils.GenUUID()
		newATriggers = append(newATriggers, newAtr)
	}
	return newATriggers
}
コード例 #16
0
ファイル: apier.go プロジェクト: cgrates/cgrates
func (self *ApierV1) SetActionPlan(attrs AttrSetActionPlan, reply *string) (err error) {
	if missing := utils.MissingStructFields(&attrs, []string{"Id", "ActionPlan"}); len(missing) != 0 {
		return utils.NewErrMandatoryIeMissing(missing...)
	}
	for _, at := range attrs.ActionPlan {
		requiredFields := []string{"ActionsId", "Time", "Weight"}
		if missing := utils.MissingStructFields(at, requiredFields); len(missing) != 0 {
			return fmt.Errorf("%s:Action:%s:%v", utils.ErrMandatoryIeMissing.Error(), at.ActionsId, missing)
		}
	}
	if !attrs.Overwrite {
		if exists, err := self.RatingDb.HasData(utils.ACTION_PLAN_PREFIX, attrs.Id); err != nil {
			return utils.NewErrServerError(err)
		} else if exists {
			return utils.ErrExists
		}
	}
	ap := &engine.ActionPlan{
		Id: attrs.Id,
	}
	for _, apiAtm := range attrs.ActionPlan {
		if exists, err := self.RatingDb.HasData(utils.ACTION_PREFIX, apiAtm.ActionsId); err != nil {
			return utils.NewErrServerError(err)
		} else if !exists {
			return fmt.Errorf("%s:%s", utils.ErrBrokenReference.Error(), apiAtm.ActionsId)
		}
		timing := new(engine.RITiming)
		timing.Years.Parse(apiAtm.Years, ";")
		timing.Months.Parse(apiAtm.Months, ";")
		timing.MonthDays.Parse(apiAtm.MonthDays, ";")
		timing.WeekDays.Parse(apiAtm.WeekDays, ";")
		timing.StartTime = apiAtm.Time
		ap.ActionTimings = append(ap.ActionTimings, &engine.ActionTiming{
			Uuid:      utils.GenUUID(),
			Weight:    apiAtm.Weight,
			Timing:    &engine.RateInterval{Timing: timing},
			ActionsID: apiAtm.ActionsId,
		})
	}
	if err := self.RatingDb.SetActionPlan(ap.Id, ap, true, utils.NonTransactional); err != nil {
		return utils.NewErrServerError(err)
	}
	if err = self.RatingDb.CacheDataFromDB(utils.ACTION_PLAN_PREFIX, []string{ap.Id}, true); err != nil {
		return utils.NewErrServerError(err)
	}
	if attrs.ReloadScheduler {
		sched := self.ServManager.GetScheduler()
		if sched == nil {
			return errors.New(utils.SchedulerNotRunningCaps)
		}
		sched.Reload()
	}
	*reply = OK
	return nil
}
コード例 #17
0
ファイル: tp_reader.go プロジェクト: bhepp/cgrates
func (tpr *TpReader) LoadActionPlans() (err error) {
	tps, err := tpr.lr.GetTpActionPlans(tpr.tpid, "")
	if err != nil {
		return err
	}

	storAps, err := TpActionPlans(tps).GetActionPlans()
	if err != nil {
		return err
	}
	for atId, ats := range storAps {
		for _, at := range ats {

			_, exists := tpr.actions[at.ActionsId]
			if !exists && tpr.ratingStorage != nil {
				if exists, err = tpr.ratingStorage.HasData(utils.ACTION_PREFIX, at.ActionsId); err != nil {
					return fmt.Errorf("[ActionPlans] Error querying actions: %v - %s", at.ActionsId, err.Error())
				}
			}
			if !exists {
				return fmt.Errorf("[ActionPlans] Could not load the action for tag: %v", at.ActionsId)
			}
			t, exists := tpr.timings[at.TimingId]
			if !exists {
				return fmt.Errorf("[ActionPlans] Could not load the timing for tag: %v", at.TimingId)
			}
			var actPln *ActionPlan
			if actPln, exists = tpr.actionPlans[atId]; !exists {
				actPln = &ActionPlan{
					Id: atId,
				}
			}
			actPln.ActionTimings = append(actPln.ActionTimings, &ActionTiming{
				Uuid:   utils.GenUUID(),
				Weight: at.Weight,
				Timing: &RateInterval{
					Timing: &RITiming{
						Years:     t.Years,
						Months:    t.Months,
						MonthDays: t.MonthDays,
						WeekDays:  t.WeekDays,
						StartTime: t.StartTime,
					},
				},
				ActionsID: at.ActionsId,
			})

			tpr.actionPlans[atId] = actPln
		}
	}

	return nil
}
コード例 #18
0
ファイル: apier.go プロジェクト: kevinlovesing/cgrates
func (self *ApierV1) SetActionPlan(attrs AttrSetActionPlan, reply *string) error {
	if missing := utils.MissingStructFields(&attrs, []string{"Id", "ActionPlan"}); len(missing) != 0 {
		return utils.NewErrMandatoryIeMissing(missing...)
	}
	for _, at := range attrs.ActionPlan {
		requiredFields := []string{"ActionsId", "Time", "Weight"}
		if missing := utils.MissingStructFields(at, requiredFields); len(missing) != 0 {
			return fmt.Errorf("%s:Action:%s:%v", utils.ErrMandatoryIeMissing.Error(), at.ActionsId, missing)
		}
	}
	if !attrs.Overwrite {
		if exists, err := self.RatingDb.HasData(utils.ACTION_PLAN_PREFIX, attrs.Id); err != nil {
			return utils.NewErrServerError(err)
		} else if exists {
			return utils.ErrExists
		}
	}
	storeAtms := make(engine.ActionPlans, len(attrs.ActionPlan))
	for idx, apiAtm := range attrs.ActionPlan {
		if exists, err := self.RatingDb.HasData(utils.ACTION_PREFIX, apiAtm.ActionsId); err != nil {
			return utils.NewErrServerError(err)
		} else if !exists {
			return fmt.Errorf("%s:%s", utils.ErrBrokenReference.Error(), apiAtm.ActionsId)
		}
		timing := new(engine.RITiming)
		timing.Years.Parse(apiAtm.Years, ";")
		timing.Months.Parse(apiAtm.Months, ";")
		timing.MonthDays.Parse(apiAtm.MonthDays, ";")
		timing.WeekDays.Parse(apiAtm.WeekDays, ";")
		timing.StartTime = apiAtm.Time
		at := &engine.ActionPlan{
			Uuid:      utils.GenUUID(),
			Id:        attrs.Id,
			Weight:    apiAtm.Weight,
			Timing:    &engine.RateInterval{Timing: timing},
			ActionsId: apiAtm.ActionsId,
		}
		storeAtms[idx] = at
	}
	if err := self.RatingDb.SetActionPlans(attrs.Id, storeAtms); err != nil {
		return utils.NewErrServerError(err)
	}
	self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: []string{utils.ACTION_PLAN_PREFIX + attrs.Id}})
	if attrs.ReloadScheduler {
		if self.Sched == nil {
			return errors.New("SCHEDULER_NOT_ENABLED")
		}
		self.Sched.LoadActionPlans(self.RatingDb)
		self.Sched.Restart()
	}
	*reply = OK
	return nil
}
コード例 #19
0
ファイル: apier.go プロジェクト: intralanman/cgrates
func (self *ApierV1) SetActionPlan(attrs AttrSetActionPlan, reply *string) error {
	if missing := utils.MissingStructFields(&attrs, []string{"Id", "ActionPlan"}); len(missing) != 0 {
		return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing)
	}
	for _, at := range attrs.ActionPlan {
		requiredFields := []string{"ActionsId", "Time", "Weight"}
		if missing := utils.MissingStructFields(at, requiredFields); len(missing) != 0 {
			return fmt.Errorf("%s:Action:%s:%v", utils.ERR_MANDATORY_IE_MISSING, at.ActionsId, missing)
		}
	}
	if !attrs.Overwrite {
		if exists, err := self.AccountDb.HasData(engine.ACTION_TIMING_PREFIX, attrs.Id); err != nil {
			return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
		} else if exists {
			return errors.New(utils.ERR_EXISTS)
		}
	}
	storeAtms := make(engine.ActionPlan, len(attrs.ActionPlan))
	for idx, apiAtm := range attrs.ActionPlan {
		if exists, err := self.AccountDb.HasData(engine.ACTION_PREFIX, apiAtm.ActionsId); err != nil {
			return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
		} else if !exists {
			return fmt.Errorf("%s:%s", utils.ERR_BROKEN_REFERENCE, err.Error())
		}
		timing := new(engine.RITiming)
		timing.Years.Parse(apiAtm.Years, ";")
		timing.Months.Parse(apiAtm.Months, ";")
		timing.MonthDays.Parse(apiAtm.MonthDays, ";")
		timing.WeekDays.Parse(apiAtm.WeekDays, ";")
		timing.StartTime = apiAtm.Time
		at := &engine.ActionTiming{
			Uuid:      utils.GenUUID(),
			Id:        attrs.Id,
			Weight:    apiAtm.Weight,
			Timing:    &engine.RateInterval{Timing: timing},
			ActionsId: apiAtm.ActionsId,
		}
		storeAtms[idx] = at
	}
	if err := self.AccountDb.SetActionTimings(attrs.Id, storeAtms); err != nil {
		return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
	}
	if attrs.ReloadScheduler {
		if self.Sched == nil {
			return errors.New("SCHEDULER_NOT_ENABLED")
		}
		self.Sched.LoadActionTimings(self.AccountDb)
		self.Sched.Restart()
	}
	*reply = OK
	return nil
}
コード例 #20
0
ファイル: actions_test.go プロジェクト: foehn/cgrates
func BenchmarkUUID(b *testing.B) {
	m := make(map[string]int, 1000)
	for i := 0; i < b.N; i++ {
		uuid := utils.GenUUID()
		if len(uuid) == 0 {
			b.Fatalf("GenUUID error %s", uuid)
		}
		b.StopTimer()
		c := m[uuid]
		if c > 0 {
			b.Fatalf("duplicate uuid[%s] count %d", uuid, c)
		}
		m[uuid] = c + 1
		b.StartTimer()
	}
}
コード例 #21
0
ファイル: account.go プロジェクト: eloycoto/cgrates
func (ub *Account) GetDefaultMoneyBalance() *Balance {
	for _, balance := range ub.BalanceMap[utils.MONETARY] {
		if balance.IsDefault() {
			return balance
		}
	}
	// create default balance
	defaultBalance := &Balance{
		Uuid: utils.GenUUID(),
		ID:   utils.META_DEFAULT,
	} // minimum weight
	if ub.BalanceMap == nil {
		ub.BalanceMap = make(map[string]Balances)
	}
	ub.BalanceMap[utils.MONETARY] = append(ub.BalanceMap[utils.MONETARY], defaultBalance)
	return defaultBalance
}
コード例 #22
0
ファイル: account.go プロジェクト: nikbyte/cgrates
func (ub *Account) GetDefaultMoneyBalance(direction string) *Balance {
	for _, balance := range ub.BalanceMap[utils.MONETARY+direction] {
		if balance.IsDefault() {
			return balance
		}
	}
	// create default balance
	defaultBalance := &Balance{
		Uuid:   "DEFAULT" + utils.GenUUID(),
		Weight: 0,
	} // minimum weight
	if ub.BalanceMap == nil {
		ub.BalanceMap = make(map[string]BalanceChain)
	}
	ub.BalanceMap[utils.MONETARY+direction] = append(ub.BalanceMap[utils.MONETARY+direction], defaultBalance)
	return defaultBalance
}
コード例 #23
0
ファイル: account.go プロジェクト: intralanman/cgrates
// 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 (ub *Account) debitBalanceAction(a *Action) error {
	if a == nil {
		return errors.New("nil minute action!")
	}
	if a.Balance.Uuid == "" {
		a.Balance.Uuid = utils.GenUUID()
	}
	if ub.BalanceMap == nil {
		ub.BalanceMap = make(map[string]BalanceChain, 1)
	}
	found := false
	id := a.BalanceType + a.Direction
	ub.CleanExpiredBalances()
	for _, b := range ub.BalanceMap[id] {
		if b.IsExpired() {
			continue // just to be safe (cleaned expired balances above)
		}
		if b.Equal(a.Balance) {
			b.SubstractAmount(a.Balance.Value)
			found = true
			break
		}
	}
	// if it is not found then we add it to the list
	if !found {
		a.Balance.Value = -a.Balance.Value
		a.Balance.dirty = true // Mark the balance as dirty since we have modified and it should be checked by action triggers
		ub.BalanceMap[id] = append(ub.BalanceMap[id], a.Balance)
		if a.Balance.SharedGroup != "" {
			// add shared group member
			sg, err := accountingStorage.GetSharedGroup(a.Balance.SharedGroup, false)
			if err != nil || sg == nil {
				//than problem
				Logger.Warning(fmt.Sprintf("Could not get shared group: %v", a.Balance.SharedGroup))
			} else {
				// add member and save
				sg.MemberIds = append(sg.MemberIds, ub.Id)
				accountingStorage.SetSharedGroup(sg)
			}
		}
	}
	ub.executeActionTriggers(nil)
	return nil //ub.BalanceMap[id].GetTotalValue()
}
コード例 #24
0
ファイル: loader_csv.go プロジェクト: intralanman/cgrates
func (csvr *CSVReader) LoadActionTimings() (err error) {
	csvReader, fp, err := csvr.readerFunc(csvr.actiontimingsFn, csvr.sep, utils.ACTION_PLANS_NRCOLS)
	if err != nil {
		log.Print("Could not load action plans file: ", err)
		// allow writing of the other values
		return nil
	}
	if fp != nil {
		defer fp.Close()
	}
	for record, err := csvReader.Read(); err == nil; record, err = csvReader.Read() {
		tag := record[0]
		_, exists := csvr.actions[record[1]]
		if !exists {
			return fmt.Errorf("ActionPlan: Could not load the action for tag: %v", record[1])
		}
		t, exists := csvr.timings[record[2]]
		if !exists {
			return fmt.Errorf("ActionPlan: Could not load the timing for tag: %v", record[2])
		}
		weight, err := strconv.ParseFloat(record[3], 64)
		if err != nil {
			return fmt.Errorf("ActionTiming: Could not parse action timing weight: %v", err)
		}
		at := &ActionTiming{
			Uuid:   utils.GenUUID(),
			Id:     record[0],
			Weight: weight,
			Timing: &RateInterval{
				Timing: &RITiming{
					Years:     t.Years,
					Months:    t.Months,
					MonthDays: t.MonthDays,
					WeekDays:  t.WeekDays,
					StartTime: t.StartTime,
				},
			},
			ActionsId: record[1],
		}
		csvr.actionsTimings[tag] = append(csvr.actionsTimings[tag], at)
	}
	return
}
コード例 #25
0
ファイル: apier.go プロジェクト: intralanman/cgrates
func (self *ApierV1) AddTriggeredAction(attr AttrAddActionTrigger, reply *string) error {
	if attr.Direction == "" {
		attr.Direction = engine.OUTBOUND
	}
	balExpiryTime, err := utils.ParseTimeDetectLayout(attr.BalanceExpiryTime)
	if err != nil {
		return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
	}
	at := &engine.ActionTrigger{
		Id:                    utils.GenUUID(),
		BalanceType:           attr.BalanceType,
		Direction:             attr.Direction,
		ThresholdType:         attr.ThresholdType,
		ThresholdValue:        attr.ThresholdValue,
		DestinationId:         attr.DestinationId,
		BalanceWeight:         attr.BalanceWeight,
		BalanceExpirationDate: balExpiryTime,
		Weight:                attr.Weight,
		ActionsId:             attr.ActionsId,
		Executed:              false,
	}

	tag := utils.AccountKey(attr.Tenant, attr.Account, attr.Direction)
	_, err = engine.AccLock.Guard(tag, func() (float64, error) {
		userBalance, err := self.AccountDb.GetAccount(tag)
		if err != nil {
			return 0, err
		}

		userBalance.ActionTriggers = append(userBalance.ActionTriggers, at)

		if err = self.AccountDb.SetAccount(userBalance); err != nil {
			return 0, err
		}
		return 0, nil
	})
	if err != nil {
		*reply = err.Error()
		return err
	}
	*reply = OK
	return nil
}
コード例 #26
0
// Connects to the freeswitch mod_event_socket server and starts
// listening for events.
func (sm *FSSessionManager) Connect() error {
	eventFilters := map[string]string{"Call-Direction": "inbound"}
	errChan := make(chan error)
	for _, connCfg := range sm.cfg.Connections {
		connId := utils.GenUUID()
		fSock, err := fsock.NewFSock(connCfg.Server, connCfg.Password, connCfg.Reconnects, sm.createHandlers(), eventFilters, utils.Logger.(*syslog.Writer), connId)
		if err != nil {
			return err
		} else if !fSock.Connected() {
			return errors.New("Could not connect to FreeSWITCH")
		} else {
			sm.conns[connId] = fSock
		}
		go func() { // Start reading in own goroutine, return on error
			if err := sm.conns[connId].ReadEvents(); err != nil {
				errChan <- err
			}
		}()
		if fsSenderPool, err := fsock.NewFSockPool(5, connCfg.Server, connCfg.Password, 1,
			make(map[string][]func(string, string)), make(map[string]string), utils.Logger.(*syslog.Writer), connId); err != nil {
			return fmt.Errorf("Cannot connect FreeSWITCH senders pool, error: %s", err.Error())
		} else if fsSenderPool == nil {
			return errors.New("Cannot connect FreeSWITCH senders pool.")
		} else {
			sm.senderPools[connId] = fsSenderPool
		}
		if sm.cfg.ChannelSyncInterval != 0 { // Schedule running of the callsync
			go func() {
				for { // Schedule sync channels to run repetately
					time.Sleep(sm.cfg.ChannelSyncInterval)
					sm.SyncSessions()
				}

			}()
		}
	}
	err := <-errChan // Will keep the Connect locked until the first error in one of the connections
	return err
}
コード例 #27
0
ファイル: kamailiosm.go プロジェクト: bhepp/cgrates
func (self *KamailioSessionManager) Connect() error {
	var err error
	eventHandlers := map[*regexp.Regexp][]func([]byte, string){
		regexp.MustCompile("CGR_AUTH_REQUEST"): []func([]byte, string){self.onCgrAuth},
		regexp.MustCompile("CGR_LCR_REQUEST"):  []func([]byte, string){self.onCgrLcrReq},
		regexp.MustCompile("CGR_CALL_START"):   []func([]byte, string){self.onCallStart},
		regexp.MustCompile("CGR_CALL_END"):     []func([]byte, string){self.onCallEnd},
	}
	errChan := make(chan error)
	for _, connCfg := range self.cfg.Connections {
		connId := utils.GenUUID()
		if self.conns[connId], err = kamevapi.NewKamEvapi(connCfg.EvapiAddr, connId, connCfg.Reconnects, eventHandlers, utils.Logger.(*syslog.Writer)); err != nil {
			return err
		}
		go func() { // Start reading in own goroutine, return on error
			if err := self.conns[connId].ReadEvents(); err != nil {
				errChan <- err
			}
		}()
	}
	err = <-errChan // Will keep the Connect locked until the first error in one of the connections
	return err
}
コード例 #28
0
ファイル: loader_db.go プロジェクト: intralanman/cgrates
func (dbr *DbReader) LoadActionTimings() (err error) {
	atsMap, err := dbr.storDb.GetTPActionTimings(dbr.tpid, "")
	if err != nil {
		return err
	}
	for atId, ats := range atsMap {
		for _, at := range ats {

			_, exists := dbr.actions[at.ActionsId]
			if !exists {
				return fmt.Errorf("ActionTiming: Could not load the action for tag: %v", at.ActionsId)
			}
			t, exists := dbr.timings[at.TimingId]
			if !exists {
				return fmt.Errorf("ActionTiming: Could not load the timing for tag: %v", at.TimingId)
			}
			actTmg := &ActionTiming{
				Uuid:   utils.GenUUID(),
				Id:     atId,
				Weight: at.Weight,
				Timing: &RateInterval{
					Timing: &RITiming{
						Years:     t.Years,
						Months:    t.Months,
						MonthDays: t.MonthDays,
						WeekDays:  t.WeekDays,
						StartTime: t.StartTime,
					},
				},
				ActionsId: at.ActionsId,
			}
			dbr.actionsTimings[atId] = append(dbr.actionsTimings[atId], actTmg)
		}
	}
	return err
}
コード例 #29
0
ファイル: accounts.go プロジェクト: cgrates/cgrates
func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error {
	if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 {
		return utils.NewErrMandatoryIeMissing(missing...)
	}
	accID := utils.AccountKey(attr.Tenant, attr.Account)
	dirtyActionPlans := make(map[string]*engine.ActionPlan)
	var ub *engine.Account
	_, err := engine.Guardian.Guard(func() (interface{}, error) {
		if bal, _ := self.AccountDb.GetAccount(accID); bal != nil {
			ub = bal
		} else { // Not found in db, create it here
			ub = &engine.Account{
				ID: accID,
			}
		}
		if attr.ActionPlanIDs != nil {
			_, err := engine.Guardian.Guard(func() (interface{}, error) {
				actionPlansMap, err := self.RatingDb.GetAllActionPlans()
				if err != nil {
					if err == utils.ErrNotFound { // if no action plans just continue
						return 0, nil
					}
					return 0, err
				}
				if attr.ActionPlansOverwrite {
					// clean previous action plans
					for actionPlanID, ap := range actionPlansMap {
						if _, exists := ap.AccountIDs[accID]; exists {
							delete(ap.AccountIDs, accID)
							dirtyActionPlans[actionPlanID] = ap
						}
					}
				}
				for _, actionPlanID := range *attr.ActionPlanIDs {
					ap, ok := actionPlansMap[actionPlanID]
					if !ok {
						return 0, utils.ErrNotFound
					}

					if _, exists := ap.AccountIDs[accID]; !exists {
						if ap.AccountIDs == nil {
							ap.AccountIDs = make(utils.StringMap)
						}
						ap.AccountIDs[accID] = true
						dirtyActionPlans[actionPlanID] = ap
						// create tasks
						for _, at := range ap.ActionTimings {
							if at.IsASAP() {
								t := &engine.Task{
									Uuid:      utils.GenUUID(),
									AccountID: accID,
									ActionsID: at.ActionsID,
								}
								if err = self.RatingDb.PushTask(t); err != nil {
									return 0, err
								}
							}
						}
					}
				}
				apIDs := make([]string, len(dirtyActionPlans))
				i := 0
				for actionPlanID, ap := range dirtyActionPlans {
					if err := self.RatingDb.SetActionPlan(actionPlanID, ap, true, utils.NonTransactional); err != nil {
						return 0, err
					}
					apIDs[i] = actionPlanID
					i++
				}
				if err := self.RatingDb.CacheDataFromDB(utils.ACTION_PLAN_PREFIX, apIDs, true); err != nil {
					return 0, err
				}
				return 0, nil
			}, 0, utils.ACTION_PLAN_PREFIX)
			if err != nil {
				return 0, err
			}
		}

		if attr.ActionTriggerIDs != nil {
			if attr.ActionTriggerOverwrite {
				ub.ActionTriggers = make(engine.ActionTriggers, 0)
			}
			for _, actionTriggerID := range *attr.ActionTriggerIDs {
				atrs, err := self.RatingDb.GetActionTriggers(actionTriggerID, false, utils.NonTransactional)
				if err != nil {
					return 0, err
				}
				for _, at := range atrs {
					var found bool
					for _, existingAt := range ub.ActionTriggers {
						if existingAt.Equals(at) {
							found = true
							break
						}
					}
					if !found {
						ub.ActionTriggers = append(ub.ActionTriggers, at)
					}
				}
			}
		}
		ub.InitCounters()
		if attr.AllowNegative != nil {
			ub.AllowNegative = *attr.AllowNegative
		}
		if attr.Disabled != nil {
			ub.Disabled = *attr.Disabled
		}
		// All prepared, save account
		if err := self.AccountDb.SetAccount(ub); err != nil {
			return 0, err
		}
		return 0, nil
	}, 0, accID)
	if err != nil {
		return utils.NewErrServerError(err)
	}
	if attr.ReloadScheduler && len(dirtyActionPlans) > 0 {
		sched := self.ServManager.GetScheduler()
		if sched == nil {
			return errors.New(utils.SchedulerNotRunningCaps)
		}
		sched.Reload()
	}
	*reply = utils.OK // This will mark saving of the account, error still can show up in actionTimingsId
	return nil
}
コード例 #30
0
ファイル: triggers.go プロジェクト: eloycoto/cgrates
func (self *ApierV1) SetActionTrigger(attr AttrSetActionTrigger, reply *string) error {

	if missing := utils.MissingStructFields(&attr, []string{"GroupID"}); len(missing) != 0 {
		return utils.NewErrMandatoryIeMissing(missing...)
	}

	atrs, _ := self.RatingDb.GetActionTriggers(attr.GroupID, false, utils.NonTransactional)
	var newAtr *engine.ActionTrigger
	if attr.UniqueID != "" {
		//search for exiting one
		for _, atr := range atrs {
			if atr.UniqueID == attr.UniqueID {
				newAtr = atr
				break
			}
		}
	}

	if newAtr == nil {
		newAtr = &engine.ActionTrigger{}
		atrs = append(atrs, newAtr)
	}
	newAtr.ID = attr.GroupID
	if attr.UniqueID != "" {
		newAtr.UniqueID = attr.UniqueID
	} else {
		newAtr.UniqueID = utils.GenUUID()
	}

	if attr.ThresholdType != nil {
		newAtr.ThresholdType = *attr.ThresholdType
	}
	if attr.ThresholdValue != nil {
		newAtr.ThresholdValue = *attr.ThresholdValue
	}
	if attr.Recurrent != nil {
		newAtr.Recurrent = *attr.Recurrent
	}
	if attr.MinSleep != nil {
		minSleep, err := utils.ParseDurationWithSecs(*attr.MinSleep)
		if err != nil {
			*reply = err.Error()
			return err
		}
		newAtr.MinSleep = minSleep
	}
	if attr.ExpirationDate != nil {
		expTime, err := utils.ParseTimeDetectLayout(*attr.ExpirationDate, self.Config.DefaultTimezone)
		if err != nil {
			*reply = err.Error()
			return err
		}
		newAtr.ExpirationDate = expTime
	}
	if attr.ActivationDate != nil {
		actTime, err := utils.ParseTimeDetectLayout(*attr.ActivationDate, self.Config.DefaultTimezone)
		if err != nil {
			*reply = err.Error()
			return err
		}
		newAtr.ActivationDate = actTime
	}
	newAtr.Balance = &engine.BalanceFilter{}
	if attr.BalanceID != nil {
		newAtr.Balance.ID = attr.BalanceID
	}
	if attr.BalanceType != nil {
		newAtr.Balance.Type = attr.BalanceType
	}
	if attr.BalanceDirections != nil {
		newAtr.Balance.Directions = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDirections...))
	}
	if attr.BalanceDestinationIds != nil {
		newAtr.Balance.DestinationIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDestinationIds...))
	}
	if attr.BalanceWeight != nil {
		newAtr.Balance.Weight = attr.BalanceWeight
	}
	if attr.BalanceExpirationDate != nil {
		balanceExpTime, err := utils.ParseDate(*attr.BalanceExpirationDate)
		if err != nil {
			*reply = err.Error()
			return err
		}
		newAtr.Balance.ExpirationDate = &balanceExpTime
	}
	if attr.BalanceTimingTags != nil {
		newAtr.Balance.TimingIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceTimingTags...))
	}
	if attr.BalanceRatingSubject != nil {
		newAtr.Balance.RatingSubject = attr.BalanceRatingSubject
	}
	if attr.BalanceCategories != nil {
		newAtr.Balance.Categories = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceCategories...))
	}
	if attr.BalanceSharedGroups != nil {
		newAtr.Balance.SharedGroups = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceSharedGroups...))
	}
	if attr.BalanceBlocker != nil {
		newAtr.Balance.Blocker = attr.BalanceBlocker
	}
	if attr.BalanceDisabled != nil {
		newAtr.Balance.Disabled = attr.BalanceDisabled
	}
	if attr.MinQueuedItems != nil {
		newAtr.MinQueuedItems = *attr.MinQueuedItems
	}
	if attr.ActionsID != nil {
		newAtr.ActionsID = *attr.ActionsID
	}

	if err := self.RatingDb.SetActionTriggers(attr.GroupID, atrs, utils.NonTransactional); err != nil {
		*reply = err.Error()
		return err
	}
	//no cache for action triggers
	*reply = utils.OK
	return nil
}