コード例 #1
0
func TestTutLocalLcrHighestCost(t *testing.T) {
	if !*testLocal {
		return
	}
	tStart, _ := utils.ParseDate("2014-08-04T13:00:00Z")
	tEnd, _ := utils.ParseDate("2014-08-04T13:01:00Z")
	cd := engine.CallDescriptor{
		Direction:   "*out",
		Category:    "call",
		Tenant:      "cgrates.org",
		Subject:     "1002",
		Account:     "1002",
		Destination: "1002",
		TimeStart:   tStart,
		TimeEnd:     tEnd,
	}
	eStLcr := &engine.LCRCost{
		Entry: &engine.LCREntry{DestinationId: "DST_1002", RPCategory: "lcr_profile1", Strategy: engine.LCR_STRATEGY_HIGHEST, StrategyParams: "", Weight: 10.0},
		SupplierCosts: []*engine.LCRSupplierCost{
			&engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl1", Cost: 1.2, Duration: 60 * time.Second},
			&engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl2", Cost: 0.6, Duration: 60 * time.Second},
		},
	}
	var lcr engine.LCRCost
	if err := tutLocalRpc.Call("Responder.GetLCR", cd, &lcr); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(eStLcr.Entry, lcr.Entry) {
		t.Errorf("Expecting: %+v, received: %+v", eStLcr.Entry, lcr.Entry)
	} else if !reflect.DeepEqual(eStLcr.SupplierCosts, lcr.SupplierCosts) {
		t.Errorf("Expecting: %+v, received: %+v", eStLcr.SupplierCosts[0], lcr.SupplierCosts[0])
	}
}
コード例 #2
0
ファイル: loader_db.go プロジェクト: intralanman/cgrates
func (dbr *DbReader) LoadRatingProfileFiltered(qriedRpf *utils.TPRatingProfile) error {
	var resultRatingProfile *RatingProfile
	mpTpRpfs, err := dbr.storDb.GetTpRatingProfiles(qriedRpf) //map[string]*utils.TPRatingProfile
	if err != nil {
		return fmt.Errorf("No RateProfile for filter %v, error: %v", qriedRpf, err)
	}
	for _, tpRpf := range mpTpRpfs {
		// Logger.Debug(fmt.Sprintf("Rating profile: %v", tpRpf))
		resultRatingProfile = &RatingProfile{Id: tpRpf.KeyId()}
		for _, tpRa := range tpRpf.RatingPlanActivations {
			at, err := utils.ParseDate(tpRa.ActivationTime)
			if err != nil {
				return fmt.Errorf("Cannot parse activation time from %v", tpRa.ActivationTime)
			}
			_, exists := dbr.ratingPlans[tpRa.RatingPlanId]
			if !exists {
				if dbExists, err := dbr.dataDb.HasData(RATING_PLAN_PREFIX, tpRa.RatingPlanId); err != nil {
					return err
				} else if !dbExists {
					return fmt.Errorf("Could not load rating plans for tag: %v", tpRa.RatingPlanId)
				}
			}
			resultRatingProfile.RatingPlanActivations = append(resultRatingProfile.RatingPlanActivations,
				&RatingPlanActivation{at, tpRa.RatingPlanId,
					utils.FallbackSubjKeys(tpRpf.Direction, tpRpf.Tenant, tpRpf.Category, tpRa.FallbackSubjects)})
		}
		if err := dbr.dataDb.SetRatingProfile(resultRatingProfile); err != nil {
			return err
		}
	}
	return nil
}
コード例 #3
0
ファイル: action_plan.go プロジェクト: foehn/cgrates
func (at *ActionPlan) Execute() (err error) {
	if len(at.AccountIds) == 0 { // nothing to do if no accounts set
		return
	}
	at.resetStartTimeCache()
	aac, err := at.getActions()
	if err != nil {
		utils.Logger.Err(fmt.Sprintf("Failed to get actions for %s: %s", at.ActionsId, err))
		return
	}
	for _, a := range aac {
		if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.ExpirationDate.IsZero()) && parseErr == nil && !expDate.IsZero() {
			a.Balance.ExpirationDate = expDate
		}
		// handle remove action
		if a.ActionType == REMOVE_ACCOUNT {
			for _, accId := range at.AccountIds {
				_, err := Guardian.Guard(func() (interface{}, error) {
					if err := accountingStorage.RemoveAccount(accId); err != nil {
						utils.Logger.Warning(fmt.Sprintf("Could not remove account Id: %s: %d", accId, err))
					}
					return 0, nil
				}, 0, accId)
				if err != nil {
					utils.Logger.Warning(fmt.Sprintf("Error executing action plan: %v", err))
				}
			}
			continue // do not go to getActionFunc
		}

		actionFunction, exists := getActionFunc(a.ActionType)
		if !exists {
			// do not allow the action plan to be rescheduled
			at.Timing = nil
			utils.Logger.Crit(fmt.Sprintf("Function type %v not available, aborting execution!", a.ActionType))
			return
		}
		for _, accId := range at.AccountIds {
			_, err := Guardian.Guard(func() (interface{}, error) {
				ub, err := accountingStorage.GetAccount(accId)
				if err != nil {
					utils.Logger.Warning(fmt.Sprintf("Could not get user balances for this id: %s. Skipping!", 0, accId))
					return 0, err
				} else if ub.Disabled && a.ActionType != ENABLE_ACCOUNT {
					return 0, fmt.Errorf("Account %s is disabled", accId)
				}
				//utils.Logger.Info(fmt.Sprintf("Executing %v on %+v", a.ActionType, ub))
				err = actionFunction(ub, nil, a, aac)
				//utils.Logger.Info(fmt.Sprintf("After execute, account: %+v", ub))
				accountingStorage.SetAccount(ub)
				return 0, nil
			}, 0, accId)
			if err != nil {
				utils.Logger.Warning(fmt.Sprintf("Error executing action plan: %v", err))
			}
		}
	}
	storageLogger.LogActionPlan(utils.SCHED_SOURCE, at, aac)
	return
}
コード例 #4
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
}
コード例 #5
0
ファイル: tpimporter_csv.go プロジェクト: intralanman/cgrates
func (self *TPCSVImporter) importRatingProfiles(fn string) error {
	if self.Verbose {
		log.Printf("Processing file: <%s> ", fn)
	}
	fParser, err := NewTPCSVFileParser(self.DirPath, fn)
	if err != nil {
		return err
	}
	lineNr := 0
	rpfs := make(map[string]*utils.TPRatingProfile)
	for {
		lineNr++
		record, err := fParser.ParseNextLine()
		if err == io.EOF { // Reached end of file
			break
		} else if err != nil {
			if self.Verbose {
				log.Printf("Ignoring line %d, warning: <%s> ", lineNr, err.Error())
			}
			continue
		}
		direction, tenant, tor, subject, ratingPlanTag, fallbacksubject := record[0], record[1], record[2], record[3], record[5], record[6]
		_, err = utils.ParseDate(record[4])
		if err != nil {
			if self.Verbose {
				log.Printf("Ignoring line %d, warning: <%s> ", lineNr, err.Error())
			}
			continue
		}
		loadId := utils.CSV_LOAD //Autogenerate rating profile id
		if self.ImportId != "" {
			loadId += "_" + self.ImportId
		}
		newRp := &utils.TPRatingProfile{
			TPid:      self.TPid,
			LoadId:    loadId,
			Tenant:    tenant,
			Category:  tor,
			Direction: direction,
			Subject:   subject,
			RatingPlanActivations: []*utils.TPRatingActivation{
				&utils.TPRatingActivation{ActivationTime: record[4], RatingPlanId: ratingPlanTag, FallbackSubjects: fallbacksubject}},
		}
		if rp, hasIt := rpfs[newRp.KeyId()]; hasIt {
			rp.RatingPlanActivations = append(rp.RatingPlanActivations, newRp.RatingPlanActivations...)
		} else {
			rpfs[newRp.KeyId()] = newRp
		}
	}
	if err := self.StorDb.SetTPRatingProfiles(self.TPid, rpfs); err != nil {
		if self.Verbose {
			log.Printf("Ignoring line %d, storDb operational error: <%s> ", lineNr, err.Error())
		}
	}

	return nil
}
コード例 #6
0
ファイル: apier.go プロジェクト: nikbyte/cgrates
func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error {
	expTime, err := utils.ParseDate(attr.ExpiryTime)
	if err != nil {
		*reply = err.Error()
		return err
	}
	tag := utils.ConcatenatedKey(attr.Direction, attr.Tenant, attr.Account)
	if _, err := self.AccountDb.GetAccount(tag); err != nil {
		// create user balance if not exists
		account := &engine.Account{
			Id: tag,
		}
		if err := self.AccountDb.SetAccount(account); err != nil {
			*reply = err.Error()
			return err
		}
	}
	at := &engine.ActionPlan{
		AccountIds: []string{tag},
	}
	if attr.Direction == "" {
		attr.Direction = engine.OUTBOUND
	}
	aType := engine.DEBIT
	// reverse the sign as it is a debit
	attr.Value = -attr.Value

	if attr.Overwrite {
		aType = engine.DEBIT_RESET
	}
	at.SetActions(engine.Actions{
		&engine.Action{
			ActionType:  aType,
			BalanceType: attr.BalanceType,
			Direction:   attr.Direction,
			Balance: &engine.Balance{
				Uuid:           attr.BalanceUuid,
				Id:             attr.BalanceId,
				Value:          attr.Value,
				ExpirationDate: expTime,
				RatingSubject:  attr.RatingSubject,
				DestinationIds: attr.DestinationId,
				Weight:         attr.Weight,
				SharedGroup:    attr.SharedGroup,
				Disabled:       attr.Disabled,
			},
		},
	})
	if err := at.Execute(); err != nil {
		*reply = err.Error()
		return err
	}
	*reply = OK
	return nil
}
コード例 #7
0
ファイル: loader_csv.go プロジェクト: intralanman/cgrates
func (csvr *CSVReader) LoadRatingProfiles() (err error) {
	csvReader, fp, err := csvr.readerFunc(csvr.ratingprofilesFn, csvr.sep, utils.RATE_PROFILES_NRCOLS)
	if err != nil {
		log.Print("Could not load rating profiles 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() {
		direction, tenant, tor, subject, fallbacksubject := record[0], record[1], record[2], record[3], record[6]
		at, err := utils.ParseDate(record[4])
		if err != nil {
			return fmt.Errorf("Cannot parse activation time from %v", record[4])
		}
		// extract aliases from subject
		aliases := strings.Split(subject, ";")
		csvr.dirtyRpAliases = append(csvr.dirtyRpAliases, &TenantRatingSubject{Tenant: tenant, Subject: aliases[0]})
		if len(aliases) > 1 {
			subject = aliases[0]
			for _, alias := range aliases[1:] {
				csvr.rpAliases[utils.RatingSubjectAliasKey(tenant, alias)] = subject
			}
		}
		key := fmt.Sprintf("%s:%s:%s:%s", direction, tenant, tor, subject)
		rp, ok := csvr.ratingProfiles[key]
		if !ok {
			rp = &RatingProfile{Id: key}
			csvr.ratingProfiles[key] = rp
		}
		_, exists := csvr.ratingPlans[record[5]]
		if !exists && csvr.dataStorage != nil {
			if exists, err = csvr.dataStorage.HasData(RATING_PLAN_PREFIX, record[5]); err != nil {
				return err
			}
		}
		if !exists {
			return fmt.Errorf("Could not load rating plans for tag: %v", record[5])
		}
		rpa := &RatingPlanActivation{
			ActivationTime: at,
			RatingPlanId:   record[5],
			FallbackKeys:   utils.FallbackSubjKeys(direction, tenant, tor, fallbacksubject),
		}
		rp.RatingPlanActivations = append(rp.RatingPlanActivations, rpa)
		csvr.ratingProfiles[rp.Id] = rp
	}
	return
}
コード例 #8
0
ファイル: apier.go プロジェクト: nikbyte/cgrates
// Sets a specific rating profile working with data directly in the RatingDb without involving storDb
func (self *ApierV1) SetRatingProfile(attrs AttrSetRatingProfile, reply *string) error {
	if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "TOR", "Direction", "Subject", "RatingPlanActivations"}); len(missing) != 0 {
		return utils.NewErrMandatoryIeMissing(missing...)
	}
	for _, rpa := range attrs.RatingPlanActivations {
		if missing := utils.MissingStructFields(rpa, []string{"ActivationTime", "RatingPlanId"}); len(missing) != 0 {
			return fmt.Errorf("%s:RatingPlanActivation:%v", utils.ErrMandatoryIeMissing.Error(), missing)
		}
	}
	tpRpf := utils.TPRatingProfile{Tenant: attrs.Tenant, Category: attrs.Category, Direction: attrs.Direction, Subject: attrs.Subject}
	keyId := tpRpf.KeyId()
	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
		}
	}
	rpfl := &engine.RatingProfile{Id: keyId, RatingPlanActivations: make(engine.RatingPlanActivations, len(attrs.RatingPlanActivations))}
	for idx, ra := range attrs.RatingPlanActivations {
		at, err := utils.ParseDate(ra.ActivationTime)
		if err != nil {
			return fmt.Errorf(fmt.Sprintf("%s:Cannot parse activation time from %v", utils.ErrServerError.Error(), ra.ActivationTime))
		}
		if exists, err := self.RatingDb.HasData(utils.RATING_PLAN_PREFIX, ra.RatingPlanId); err != nil {
			return utils.NewErrServerError(err)
		} 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)}
	}
	if err := self.RatingDb.SetRatingProfile(rpfl); err != nil {
		return utils.NewErrServerError(err)
	}
	//Automatic cache of the newly inserted rating profile
	if err := self.RatingDb.CacheRatingPrefixValues(map[string][]string{
		utils.RATING_PROFILE_PREFIX: []string{utils.RATING_PROFILE_PREFIX + keyId},
	}); err != nil {
		return err
	}
	*reply = OK
	return nil
}
コード例 #9
0
ファイル: action_trigger.go プロジェクト: intralanman/cgrates
func (at *ActionTrigger) Execute(ub *Account, sq *StatsQueueTriggered) (err error) {
	// check for min sleep time
	if at.Recurrent && !at.lastExecutionTime.IsZero() && time.Since(at.lastExecutionTime) < at.MinSleep {
		return
	}
	at.lastExecutionTime = time.Now()
	if ub != nil && ub.Disabled {
		return fmt.Errorf("User %s is disabled and there are triggers in action!", ub.Id)
	}
	// does NOT need to Lock() because it is triggered from a method that took the Lock
	var aac Actions
	aac, err = accountingStorage.GetActions(at.ActionsId, false)
	aac.Sort()
	if err != nil {
		Logger.Err(fmt.Sprintf("Failed to get actions: %v", err))
		return
	}
	at.Executed = true
	atLeastOneActionExecuted := false
	for _, a := range aac {
		if a.Balance == nil {
			a.Balance = &Balance{}
		}
		a.Balance.ExpirationDate, _ = utils.ParseDate(a.ExpirationString)
		actionFunction, exists := getActionFunc(a.ActionType)
		if !exists {
			Logger.Warning(fmt.Sprintf("Function type %v not available, aborting execution!", a.ActionType))
			return
		}
		//go Logger.Info(fmt.Sprintf("Executing %v, %v: %v", ub, sq, a))
		err = actionFunction(ub, sq, a)
		if err == nil {
			atLeastOneActionExecuted = true
		}
	}
	if !atLeastOneActionExecuted || at.Recurrent {
		at.Executed = false
	}
	if ub != nil {
		storageLogger.LogActionTrigger(ub.Id, RATER_SOURCE, at, aac)
		accountingStorage.SetAccount(ub)
	}
	return
}
コード例 #10
0
ファイル: apier.go プロジェクト: intralanman/cgrates
// Sets a specific rating profile working with data directly in the RatingDb without involving storDb
func (self *ApierV1) SetRatingProfile(attrs AttrSetRatingProfile, reply *string) error {
	if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "TOR", "Direction", "Subject", "RatingPlanActivations"}); len(missing) != 0 {
		return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing)
	}
	for _, rpa := range attrs.RatingPlanActivations {
		if missing := utils.MissingStructFields(rpa, []string{"ActivationTime", "RatingPlanId"}); len(missing) != 0 {
			return fmt.Errorf("%s:RatingPlanActivation:%v", utils.ERR_MANDATORY_IE_MISSING, missing)
		}
	}
	tpRpf := utils.TPRatingProfile{Tenant: attrs.Tenant, Category: attrs.Category, Direction: attrs.Direction, Subject: attrs.Subject}
	keyId := tpRpf.KeyId()
	if !attrs.Overwrite {
		if exists, err := self.RatingDb.HasData(engine.RATING_PROFILE_PREFIX, keyId); err != nil {
			return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
		} else if exists {
			return errors.New(utils.ERR_EXISTS)
		}
	}
	rpfl := &engine.RatingProfile{Id: keyId, RatingPlanActivations: make(engine.RatingPlanActivations, len(attrs.RatingPlanActivations))}
	for idx, ra := range attrs.RatingPlanActivations {
		at, err := utils.ParseDate(ra.ActivationTime)
		if err != nil {
			return fmt.Errorf(fmt.Sprintf("%s:Cannot parse activation time from %v", utils.ERR_SERVER_ERROR, ra.ActivationTime))
		}
		if exists, err := self.RatingDb.HasData(engine.RATING_PLAN_PREFIX, ra.RatingPlanId); err != nil {
			return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
		} else if !exists {
			return fmt.Errorf(fmt.Sprintf("%s:RatingPlanId:%s", utils.ERR_NOT_FOUND, ra.RatingPlanId))
		}
		rpfl.RatingPlanActivations[idx] = &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 fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
	}
	//Automatic cache of the newly inserted rating profile
	didNotChange := []string{}
	if err := self.RatingDb.CacheRating(didNotChange, didNotChange, []string{engine.RATING_PROFILE_PREFIX + keyId}, didNotChange, didNotChange); err != nil {
		return err
	}
	*reply = OK
	return nil
}
コード例 #11
0
ファイル: tp_reader.go プロジェクト: foehn/cgrates
func (tpr *TpReader) LoadRatingProfilesFiltered(qriedRpf *TpRatingProfile) error {
	var resultRatingProfile *RatingProfile
	mpTpRpfs, err := tpr.lr.GetTpRatingProfiles(qriedRpf)
	if err != nil {
		return fmt.Errorf("no RateProfile for filter %v, error: %v", qriedRpf, err)
	}

	rpfs, err := TpRatingProfiles(mpTpRpfs).GetRatingProfiles()
	if err != nil {
		return err
	}
	for _, tpRpf := range rpfs {
		resultRatingProfile = &RatingProfile{Id: tpRpf.KeyId()}
		for _, tpRa := range tpRpf.RatingPlanActivations {
			at, err := utils.ParseDate(tpRa.ActivationTime)
			if err != nil {
				return fmt.Errorf("cannot parse activation time from %v", tpRa.ActivationTime)
			}
			_, exists := tpr.ratingPlans[tpRa.RatingPlanId]
			if !exists && tpr.ratingStorage != nil {
				if exists, err = tpr.ratingStorage.HasData(utils.RATING_PLAN_PREFIX, tpRa.RatingPlanId); err != nil {
					return err
				}
			}
			if !exists {
				return fmt.Errorf("could not load rating plans for tag: %v", tpRa.RatingPlanId)
			}
			resultRatingProfile.RatingPlanActivations = append(resultRatingProfile.RatingPlanActivations,
				&RatingPlanActivation{
					ActivationTime:  at,
					RatingPlanId:    tpRa.RatingPlanId,
					FallbackKeys:    utils.FallbackSubjKeys(tpRpf.Direction, tpRpf.Tenant, tpRpf.Category, tpRa.FallbackSubjects),
					CdrStatQueueIds: strings.Split(tpRa.CdrStatQueueIds, utils.INFIELD_SEP),
				})
		}
		if err := tpr.ratingStorage.SetRatingProfile(resultRatingProfile); err != nil {
			return err
		}
	}
	return nil
}
コード例 #12
0
ファイル: action_timing.go プロジェクト: intralanman/cgrates
func (at *ActionTiming) Execute() (err error) {
	if len(at.AccountIds) == 0 { // nothing to do if no accounts set
		return
	}
	at.resetStartTimeCache()
	aac, err := at.getActions()
	if err != nil {
		Logger.Err(fmt.Sprintf("Failed to get actions for %s: %s", at.ActionsId, err))
		return
	}
	for _, a := range aac {
		if expDate, parseErr := utils.ParseDate(a.ExpirationString); a.Balance.ExpirationDate.IsZero() && parseErr == nil && !expDate.IsZero() {
			a.Balance.ExpirationDate = expDate
		}
		actionFunction, exists := getActionFunc(a.ActionType)
		if !exists {
			Logger.Crit(fmt.Sprintf("Function type %v not available, aborting execution!", a.ActionType))
			return
		}
		for _, ubId := range at.AccountIds {
			_, err := AccLock.Guard(ubId, func() (float64, error) {
				ub, err := accountingStorage.GetAccount(ubId)
				if err != nil {
					Logger.Warning(fmt.Sprintf("Could not get user balances for this id: %s. Skipping!", ubId))
					return 0, err
				} else if ub.Disabled {
					return 0, fmt.Errorf("User %s is disabled", ubId)
				}
				//Logger.Info(fmt.Sprintf("Executing %v on %v", a.ActionType, ub.Id))
				err = actionFunction(ub, nil, a)
				accountingStorage.SetAccount(ub)
				return 0, nil
			})
			if err != nil {
				Logger.Warning(fmt.Sprintf("Error executing action timing: %v", err))
			}
		}
	}
	storageLogger.LogActionTiming(SCHED_SOURCE, at, aac)
	return
}
コード例 #13
0
ファイル: apier.go プロジェクト: henrylee2cn/cgrates
func (self *ApierV1) EnableDisableBalance(attr *AttrAddBalance, reply *string) error {
	expTime, err := utils.ParseDate(attr.ExpiryTime)
	if err != nil {
		*reply = err.Error()
		return err
	}
	tag := utils.ConcatenatedKey(attr.Direction, attr.Tenant, attr.Account)
	if _, err := self.AccountDb.GetAccount(tag); err != nil {
		return utils.ErrNotFound
	}
	at := &engine.ActionPlan{
		AccountIds: []string{tag},
	}
	if attr.Direction == "" {
		attr.Direction = utils.OUT
	}
	at.SetActions(engine.Actions{
		&engine.Action{
			ActionType:  engine.ENABLE_DISABLE_BALANCE,
			BalanceType: attr.BalanceType,
			Direction:   attr.Direction,
			Balance: &engine.Balance{
				Uuid:           attr.BalanceUuid,
				Id:             attr.BalanceId,
				Value:          attr.Value,
				ExpirationDate: expTime,
				RatingSubject:  attr.RatingSubject,
				DestinationIds: attr.DestinationId,
				Weight:         attr.Weight,
				SharedGroup:    attr.SharedGroup,
				Disabled:       attr.Disabled,
			},
		},
	})
	if err := at.Execute(); err != nil {
		*reply = err.Error()
		return err
	}
	*reply = OK
	return nil
}
コード例 #14
0
ファイル: loader_db.go プロジェクト: intralanman/cgrates
func (dbr *DbReader) LoadRatingProfiles() error {
	mpTpRpfs, err := dbr.storDb.GetTpRatingProfiles(&utils.TPRatingProfile{TPid: dbr.tpid}) //map[string]*utils.TPRatingProfile
	if err != nil {
		return err
	}
	for _, tpRpf := range mpTpRpfs {
		// extract aliases from subject
		aliases := strings.Split(tpRpf.Subject, ";")
		dbr.dirtyRpAliases = append(dbr.dirtyRpAliases, &TenantRatingSubject{Tenant: tpRpf.Tenant, Subject: aliases[0]})
		if len(aliases) > 1 {
			tpRpf.Subject = aliases[0]
			for _, alias := range aliases[1:] {
				dbr.rpAliases[utils.RatingSubjectAliasKey(tpRpf.Tenant, alias)] = tpRpf.Subject
			}
		}
		rpf := &RatingProfile{Id: tpRpf.KeyId()}
		for _, tpRa := range tpRpf.RatingPlanActivations {
			at, err := utils.ParseDate(tpRa.ActivationTime)
			if err != nil {
				return fmt.Errorf("Cannot parse activation time from %v", tpRa.ActivationTime)
			}
			_, exists := dbr.ratingPlans[tpRa.RatingPlanId]
			if !exists {
				if dbExists, err := dbr.dataDb.HasData(RATING_PLAN_PREFIX, tpRa.RatingPlanId); err != nil {
					return err
				} else if !dbExists {
					return fmt.Errorf("Could not load rating plans for tag: %v", tpRa.RatingPlanId)
				}
			}
			rpf.RatingPlanActivations = append(rpf.RatingPlanActivations,
				&RatingPlanActivation{
					ActivationTime: at,
					RatingPlanId:   tpRa.RatingPlanId,
					FallbackKeys:   utils.FallbackSubjKeys(tpRpf.Direction, tpRpf.Tenant, tpRpf.Category, tpRa.FallbackSubjects),
				})
		}
		dbr.ratingProfiles[tpRpf.KeyId()] = rpf
	}
	return nil
}
コード例 #15
0
ファイル: tp_reader.go プロジェクト: foehn/cgrates
func (tpr *TpReader) LoadRatingProfiles() (err error) {
	tps, err := tpr.lr.GetTpRatingProfiles(&TpRatingProfile{Tpid: tpr.tpid})
	if err != nil {
		return err
	}
	mpTpRpfs, err := TpRatingProfiles(tps).GetRatingProfiles()

	if err != nil {
		return err
	}
	for _, tpRpf := range mpTpRpfs {
		rpf := &RatingProfile{Id: tpRpf.KeyId()}
		for _, tpRa := range tpRpf.RatingPlanActivations {
			at, err := utils.ParseDate(tpRa.ActivationTime)
			if err != nil {
				return fmt.Errorf("cannot parse activation time from %v", tpRa.ActivationTime)
			}
			_, exists := tpr.ratingPlans[tpRa.RatingPlanId]
			if !exists && tpr.ratingStorage != nil { // Only query if there is a connection, eg on dry run there is none
				if exists, err = tpr.ratingStorage.HasData(utils.RATING_PLAN_PREFIX, tpRa.RatingPlanId); err != nil {
					return err
				}
			}
			if !exists {
				return fmt.Errorf("could not load rating plans for tag: %v", tpRa.RatingPlanId)
			}
			rpf.RatingPlanActivations = append(rpf.RatingPlanActivations,
				&RatingPlanActivation{
					ActivationTime:  at,
					RatingPlanId:    tpRa.RatingPlanId,
					FallbackKeys:    utils.FallbackSubjKeys(tpRpf.Direction, tpRpf.Tenant, tpRpf.Category, tpRa.FallbackSubjects),
					CdrStatQueueIds: strings.Split(tpRa.CdrStatQueueIds, utils.INFIELD_SEP),
				})
		}
		tpr.ratingProfiles[tpRpf.KeyId()] = rpf
	}
	return nil
}
コード例 #16
0
ファイル: apier.go プロジェクト: kevinlovesing/cgrates
func (self *ApierV1) RemoveBalances(attr *AttrAddBalance, reply *string) error {
	expTime, err := utils.ParseDate(attr.ExpiryTime)
	if err != nil {
		*reply = err.Error()
		return err
	}
	accId := utils.ConcatenatedKey(attr.Tenant, attr.Account)
	if _, err := self.AccountDb.GetAccount(accId); err != nil {
		return utils.ErrNotFound
	}
	at := &engine.ActionPlan{
		AccountIds: []string{accId},
	}
	at.SetActions(engine.Actions{
		&engine.Action{
			ActionType:  engine.REMOVE_BALANCE,
			BalanceType: attr.BalanceType,
			Balance: &engine.Balance{
				Uuid:           attr.BalanceUuid,
				Id:             attr.BalanceId,
				Value:          attr.Value,
				ExpirationDate: expTime,
				RatingSubject:  attr.RatingSubject,
				Directions:     utils.ParseStringMap(attr.Directions),
				DestinationIds: utils.ParseStringMap(attr.DestinationIds),
				Weight:         attr.Weight,
				SharedGroups:   utils.ParseStringMap(attr.SharedGroups),
				Disabled:       attr.Disabled,
			},
		},
	})
	if err := at.Execute(); err != nil {
		*reply = err.Error()
		return err
	}
	*reply = OK
	return nil
}
コード例 #17
0
ファイル: tp_reader.go プロジェクト: bhepp/cgrates
func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error {
	accountActions, err := tpr.lr.GetTpAccountActions(qriedAA)
	if err != nil {
		return errors.New(err.Error() + ": " + fmt.Sprintf("%+v", qriedAA))
	}
	storAas, err := TpAccountActions(accountActions).GetAccountActions()
	if err != nil {
		return err
	}
	for _, accountAction := range storAas {
		id := accountAction.KeyId()
		var actionsIds []string // collects action ids
		// action timings
		if accountAction.ActionPlanId != "" {
			// get old userBalanceIds
			exitingAccountIds := make(utils.StringMap)
			existingActionPlan, err := tpr.ratingStorage.GetActionPlan(accountAction.ActionPlanId, true)
			if err == nil && existingActionPlan != nil {
				exitingAccountIds = existingActionPlan.AccountIDs
			}

			tpap, err := tpr.lr.GetTpActionPlans(tpr.tpid, accountAction.ActionPlanId)
			if err != nil {
				return errors.New(err.Error() + " (ActionPlan): " + accountAction.ActionPlanId)
			} else if len(tpap) == 0 {
				return fmt.Errorf("no action plan with id <%s>", accountAction.ActionPlanId)
			}
			aps, err := TpActionPlans(tpap).GetActionPlans()
			if err != nil {
				return err
			}
			var actionPlan *ActionPlan
			ats := aps[accountAction.ActionPlanId]
			for _, at := range ats {
				// Check action exists before saving it inside actionTiming key
				// ToDo: try saving the key after the actions was retrieved in order to save one query here.
				if actions, err := tpr.lr.GetTpActions(tpr.tpid, at.ActionsId); err != nil {
					return errors.New(err.Error() + " (Actions): " + at.ActionsId)
				} else if len(actions) == 0 {
					return fmt.Errorf("no action with id <%s>", at.ActionsId)
				}
				var t *utils.TPTiming
				if at.TimingId != utils.ASAP {
					tptm, err := tpr.lr.GetTpTimings(tpr.tpid, at.TimingId)
					if err != nil {
						return errors.New(err.Error() + " (Timing): " + at.TimingId)
					} else if len(tptm) == 0 {
						return fmt.Errorf("no timing with id <%s>", at.TimingId)
					}
					tm, err := TpTimings(tptm).GetTimings()
					if err != nil {
						return err
					}
					t = tm[at.TimingId]
				} else {
					t = tpr.timings[at.TimingId] // *asap
				}
				if actionPlan == nil {
					actionPlan = &ActionPlan{
						Id: accountAction.ActionPlanId,
					}
				}
				actionPlan.ActionTimings = append(actionPlan.ActionTimings, &ActionTiming{
					Uuid:   utils.GenUUID(),
					Weight: at.Weight,
					Timing: &RateInterval{
						Timing: &RITiming{
							Months:    t.Months,
							MonthDays: t.MonthDays,
							WeekDays:  t.WeekDays,
							StartTime: t.StartTime,
						},
					},
					ActionsID: at.ActionsId,
				})
				// collect action ids from timings
				actionsIds = append(actionsIds, at.ActionsId)
				exitingAccountIds[id] = true
				actionPlan.AccountIDs = exitingAccountIds
			}

			// write tasks
			for _, at := range actionPlan.ActionTimings {
				if at.IsASAP() {
					for accID := range actionPlan.AccountIDs {
						t := &Task{
							Uuid:      utils.GenUUID(),
							AccountID: accID,
							ActionsID: at.ActionsID,
						}
						if err = tpr.ratingStorage.PushTask(t); err != nil {
							return err
						}
					}
				}
			}
			// write action plan
			err = tpr.ratingStorage.SetActionPlan(accountAction.ActionPlanId, actionPlan, false)
			if err != nil {
				return errors.New(err.Error() + " (SetActionPlan): " + accountAction.ActionPlanId)
			}
		}
		// action triggers
		var actionTriggers ActionTriggers
		//ActionTriggerPriotityList []*ActionTrigger
		if accountAction.ActionTriggersId != "" {
			tpatrs, err := tpr.lr.GetTpActionTriggers(tpr.tpid, accountAction.ActionTriggersId)
			if err != nil {
				return errors.New(err.Error() + " (ActionTriggers): " + accountAction.ActionTriggersId)
			}
			atrs, err := TpActionTriggers(tpatrs).GetActionTriggers()
			if err != nil {
				return err
			}

			atrsMap := make(map[string][]*ActionTrigger)
			for key, atrsLst := range atrs {
				atrs := make([]*ActionTrigger, len(atrsLst))
				for idx, apiAtr := range atrsLst {
					minSleep, _ := utils.ParseDurationWithSecs(apiAtr.MinSleep)
					balanceExpTime, _ := utils.ParseDate(apiAtr.BalanceExpirationDate)
					expTime, _ := utils.ParseTimeDetectLayout(apiAtr.ExpirationDate, tpr.timezone)
					actTime, _ := utils.ParseTimeDetectLayout(apiAtr.ActivationDate, tpr.timezone)
					if apiAtr.UniqueID == "" {
						apiAtr.UniqueID = utils.GenUUID()
					}
					atrs[idx] = &ActionTrigger{
						ID:                    key,
						UniqueID:              apiAtr.UniqueID,
						ThresholdType:         apiAtr.ThresholdType,
						ThresholdValue:        apiAtr.ThresholdValue,
						Recurrent:             apiAtr.Recurrent,
						MinSleep:              minSleep,
						ExpirationDate:        expTime,
						ActivationDate:        actTime,
						BalanceId:             apiAtr.BalanceId,
						BalanceType:           apiAtr.BalanceType,
						BalanceDirections:     utils.ParseStringMap(apiAtr.BalanceDirections),
						BalanceDestinationIds: utils.ParseStringMap(apiAtr.BalanceDestinationIds),
						BalanceWeight:         apiAtr.BalanceWeight,
						BalanceExpirationDate: balanceExpTime,
						BalanceTimingTags:     utils.ParseStringMap(apiAtr.BalanceTimingTags),
						BalanceRatingSubject:  apiAtr.BalanceRatingSubject,
						BalanceCategories:     utils.ParseStringMap(apiAtr.BalanceCategories),
						BalanceSharedGroups:   utils.ParseStringMap(apiAtr.BalanceSharedGroups),
						BalanceBlocker:        apiAtr.BalanceBlocker,
						BalanceDisabled:       apiAtr.BalanceDisabled,
						Weight:                apiAtr.Weight,
						ActionsId:             apiAtr.ActionsId,
					}
				}
				atrsMap[key] = atrs
			}
			actionTriggers = atrsMap[accountAction.ActionTriggersId]
			// collect action ids from triggers
			for _, atr := range actionTriggers {
				actionsIds = append(actionsIds, atr.ActionsId)
			}
			// write action triggers
			err = tpr.ratingStorage.SetActionTriggers(accountAction.ActionTriggersId, actionTriggers)
			if err != nil {
				return errors.New(err.Error() + " (SetActionTriggers): " + accountAction.ActionTriggersId)
			}
		}

		// actions
		acts := make(map[string][]*Action)
		for _, actId := range actionsIds {
			tpas, err := tpr.lr.GetTpActions(tpr.tpid, actId)
			if err != nil {
				return err
			}
			as, err := TpActions(tpas).GetActions()
			if err != nil {
				return err
			}
			for tag, tpacts := range as {
				enacts := make([]*Action, len(tpacts))
				for idx, tpact := range tpacts {
					// check filter field
					if len(tpact.Filter) > 0 {
						if _, err := structmatcher.NewStructMatcher(tpact.Filter); err != nil {
							return fmt.Errorf("error parsing action %s filter field: %v", tag, err)
						}
					}
					enacts[idx] = &Action{
						Id:               tag + strconv.Itoa(idx),
						ActionType:       tpact.Identifier,
						BalanceType:      tpact.BalanceType,
						Weight:           tpact.Weight,
						ExtraParameters:  tpact.ExtraParameters,
						ExpirationString: tpact.ExpiryTime,
						Filter:           tpact.Filter,
						Balance: &Balance{
							Id:             tpact.BalanceId,
							Value:          tpact.Units,
							Weight:         tpact.BalanceWeight,
							RatingSubject:  tpact.RatingSubject,
							Categories:     utils.ParseStringMap(tpact.Categories),
							Directions:     utils.ParseStringMap(tpact.Directions),
							DestinationIds: utils.ParseStringMap(tpact.DestinationIds),
							SharedGroups:   utils.ParseStringMap(tpact.SharedGroups),
							TimingIDs:      utils.ParseStringMap(tpact.TimingTags),
							Blocker:        tpact.BalanceBlocker,
							Disabled:       tpact.BalanceDisabled,
						},
					}
				}
				acts[tag] = enacts
			}
		}
		// write actions
		for k, as := range acts {
			err = tpr.ratingStorage.SetActions(k, as)
			if err != nil {
				return err
			}
		}
		ub, err := tpr.accountingStorage.GetAccount(id)
		if err != nil {
			ub = &Account{
				Id: id,
			}
		}
		ub.ActionTriggers = actionTriggers
		// init counters
		ub.InitCounters()
		if err := tpr.accountingStorage.SetAccount(ub); err != nil {
			return err
		}
	}
	return nil
}
コード例 #18
0
ファイル: action_plan.go プロジェクト: kevinlovesing/cgrates
func (at *ActionPlan) Execute() (err error) {
	if len(at.AccountIds) == 0 { // nothing to do if no accounts set
		return
	}
	at.resetStartTimeCache()
	aac, err := at.getActions()
	if err != nil {
		utils.Logger.Err(fmt.Sprintf("Failed to get actions for %s: %s", at.ActionsId, err))
		return
	}
	_, err = Guardian.Guard(func() (interface{}, error) {
		for _, accId := range at.AccountIds {
			ub, err := accountingStorage.GetAccount(accId)
			if err != nil {
				utils.Logger.Warning(fmt.Sprintf("Could not get user balances for this id: %s. Skipping!", accId))
				return 0, err
			}
			transactionFailed := false
			toBeSaved := true
			for _, a := range aac {
				if ub.Disabled && a.ActionType != ENABLE_ACCOUNT {
					continue // disabled acocunts are not removed from action  plan
					//return 0, fmt.Errorf("Account %s is disabled", accId)
				}
				if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.ExpirationDate.IsZero()) && parseErr == nil && !expDate.IsZero() {
					a.Balance.ExpirationDate = expDate
				}
				// handle remove action
				if a.ActionType == REMOVE_ACCOUNT {
					if err := accountingStorage.RemoveAccount(accId); err != nil {
						utils.Logger.Err(fmt.Sprintf("Could not remove account Id: %s: %v", accId, err))
						transactionFailed = true
						break
					}
					// clean the account id from all action plans
					allATs, err := ratingStorage.GetAllActionPlans()
					if err != nil && err != utils.ErrNotFound {
						utils.Logger.Err(fmt.Sprintf("Could not get action plans: %s: %v", accId, err))
						transactionFailed = true
						break
					}
					for key, ats := range allATs {
						changed := false
						for _, at := range ats {
							for i := 0; i < len(at.AccountIds); i++ {
								if at.AccountIds[i] == accId {
									// delete without preserving order
									at.AccountIds[i] = at.AccountIds[len(at.AccountIds)-1]
									at.AccountIds = at.AccountIds[:len(at.AccountIds)-1]
									i -= 1
									changed = true
								}
							}
						}
						if changed {
							// save action plan
							ratingStorage.SetActionPlans(key, ats)
							// cache
							ratingStorage.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: []string{utils.ACTION_PLAN_PREFIX + key}})
						}
					}
					toBeSaved = false
					continue // do not go to getActionFunc
					// TODO: maybe we should break here as the account is gone
					// will leave continue for now as the next action can create another acount
				}
				actionFunction, exists := getActionFunc(a.ActionType)
				if !exists {
					// do not allow the action plan to be rescheduled
					at.Timing = nil
					utils.Logger.Err(fmt.Sprintf("Function type %v not available, aborting execution!", a.ActionType))
					transactionFailed = true
					break
				}
				if err := actionFunction(ub, nil, a, aac); err != nil {
					utils.Logger.Err(fmt.Sprintf("Error executing action %s: %v!", a.ActionType, err))
					transactionFailed = true
					break
				}
				toBeSaved = true
			}
			if !transactionFailed && toBeSaved {
				accountingStorage.SetAccount(ub)
			}
		}
		return 0, nil
	}, 0, at.AccountIds...)
	if err != nil {
		utils.Logger.Warning(fmt.Sprintf("Error executing action plan: %v", err))
		return err
	}
	storageLogger.LogActionPlan(utils.SCHED_SOURCE, at, aac)
	return
}
コード例 #19
0
ファイル: action_plan.go プロジェクト: iwada/cgrates
func (at *ActionTiming) Execute() (err error) {
	at.ResetStartTimeCache()
	aac, err := at.getActions()
	if err != nil {
		utils.Logger.Err(fmt.Sprintf("Failed to get actions for %s: %s", at.ActionsID, err))
		return
	}
	for accID, _ := range at.accountIDs {
		_, err = Guardian.Guard(func() (interface{}, error) {
			acc, err := accountingStorage.GetAccount(accID)
			if err != nil {
				utils.Logger.Warning(fmt.Sprintf("Could not get account id: %s. Skipping!", accID))
				return 0, err
			}
			transactionFailed := false
			removeAccountActionFound := false
			for _, a := range aac {
				// check action filter
				if len(a.Filter) > 0 {
					matched, err := acc.matchActionFilter(a.Filter)
					//log.Print("Checkng: ", a.Filter, matched)
					if err != nil {
						return 0, err
					}
					if !matched {
						continue
					}
				}
				if a.Balance == nil {
					a.Balance = &BalanceFilter{}
				}
				if a.ExpirationString != "" { // if it's *unlimited then it has to be zero time
					if expDate, parseErr := utils.ParseDate(a.ExpirationString); parseErr == nil {
						a.Balance.ExpirationDate = &time.Time{}
						*a.Balance.ExpirationDate = expDate
					}
				}

				actionFunction, exists := getActionFunc(a.ActionType)
				if !exists {
					// do not allow the action plan to be rescheduled
					at.Timing = nil
					utils.Logger.Err(fmt.Sprintf("Function type %v not available, aborting execution!", a.ActionType))
					transactionFailed = true
					break
				}
				if err := actionFunction(acc, nil, a, aac); err != nil {
					utils.Logger.Err(fmt.Sprintf("Error executing action %s: %v!", a.ActionType, err))
					transactionFailed = true
					break
				}
				if a.ActionType == REMOVE_ACCOUNT {
					removeAccountActionFound = true
				}
			}
			if !transactionFailed && !removeAccountActionFound {
				accountingStorage.SetAccount(acc)
			}
			return 0, nil
		}, 0, accID)
	}
	if len(at.accountIDs) == 0 { // action timing executing without accounts
		for _, a := range aac {
			if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.EmptyExpirationDate()) &&
				parseErr == nil && !expDate.IsZero() {
				a.Balance.ExpirationDate = &time.Time{}
				*a.Balance.ExpirationDate = expDate
			}

			actionFunction, exists := getActionFunc(a.ActionType)
			if !exists {
				// do not allow the action plan to be rescheduled
				at.Timing = nil
				utils.Logger.Err(fmt.Sprintf("Function type %v not available, aborting execution!", a.ActionType))
				break
			}
			if err := actionFunction(nil, nil, a, aac); err != nil {
				utils.Logger.Err(fmt.Sprintf("Error executing accountless action %s: %v!", a.ActionType, err))
				break
			}
		}
	}
	if err != nil {
		utils.Logger.Warning(fmt.Sprintf("Error executing action plan: %v", err))
		return err
	}
	storageLogger.LogActionTiming(utils.SCHED_SOURCE, at, aac)
	return
}
コード例 #20
0
ファイル: action_trigger.go プロジェクト: eloycoto/cgrates
func (at *ActionTrigger) Execute(ub *Account, sq *StatsQueueTriggered) (err error) {
	// check for min sleep time
	if at.Recurrent && !at.LastExecutionTime.IsZero() && time.Since(at.LastExecutionTime) < at.MinSleep {
		return
	}
	at.LastExecutionTime = time.Now()
	if ub != nil && ub.Disabled {
		return fmt.Errorf("User %s is disabled and there are triggers in action!", ub.ID)
	}
	// does NOT need to Lock() because it is triggered from a method that took the Lock
	var aac Actions
	aac, err = ratingStorage.GetActions(at.ActionsID, false, utils.NonTransactional)
	if err != nil {
		utils.Logger.Err(fmt.Sprintf("Failed to get actions: %v", err))
		return
	}
	aac.Sort()
	at.Executed = true
	transactionFailed := false
	removeAccountActionFound := false
	for _, a := range aac {
		// check action filter
		if len(a.Filter) > 0 {
			matched, err := ub.matchActionFilter(a.Filter)
			if err != nil {
				return err
			}
			if !matched {
				continue
			}
		}
		if a.Balance == nil {
			a.Balance = &BalanceFilter{}
		}
		if a.ExpirationString != "" { // if it's *unlimited then it has to be zero time'
			if expDate, parseErr := utils.ParseDate(a.ExpirationString); parseErr == nil {
				a.Balance.ExpirationDate = &time.Time{}
				*a.Balance.ExpirationDate = expDate
			}
		}

		actionFunction, exists := getActionFunc(a.ActionType)
		if !exists {
			utils.Logger.Err(fmt.Sprintf("Function type %v not available, aborting execution!", a.ActionType))
			transactionFailed = false
			break
		}
		//go utils.Logger.Info(fmt.Sprintf("Executing %v, %v: %v", ub, sq, a))
		if err := actionFunction(ub, sq, a, aac); err != nil {
			utils.Logger.Err(fmt.Sprintf("Error executing action %s: %v!", a.ActionType, err))
			transactionFailed = false
			break
		}
		if a.ActionType == REMOVE_ACCOUNT {
			removeAccountActionFound = true
		}
	}
	if transactionFailed || at.Recurrent {
		at.Executed = false
	}
	if !transactionFailed && ub != nil && !removeAccountActionFound {
		Publish(CgrEvent{
			"EventName": utils.EVT_ACTION_TRIGGER_FIRED,
			"Uuid":      at.UniqueID,
			"Id":        at.ID,
			"ActionIds": at.ActionsID,
		})
		accountingStorage.SetAccount(ub)
	}
	return
}
コード例 #21
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
}
コード例 #22
0
ファイル: triggers.go プロジェクト: eloycoto/cgrates
func (self *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers, 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)
	var account *engine.Account
	_, err := engine.Guardian.Guard(func() (interface{}, error) {
		if acc, err := self.AccountDb.GetAccount(accID); err == nil {
			account = acc
		} else {
			return 0, err
		}
		for _, at := range account.ActionTriggers {
			if (attr.UniqueID == "" || at.UniqueID == attr.UniqueID) &&
				(attr.GroupID == "" || at.ID == attr.GroupID) {
				// we have a winner
				if attr.ThresholdType != nil {
					at.ThresholdType = *attr.ThresholdType
				}
				if attr.ThresholdValue != nil {
					at.ThresholdValue = *attr.ThresholdValue
				}
				if attr.Recurrent != nil {
					at.Recurrent = *attr.Recurrent
				}
				if attr.Executed != nil {
					at.Executed = *attr.Executed
				}
				if attr.MinSleep != nil {
					minSleep, err := utils.ParseDurationWithSecs(*attr.MinSleep)
					if err != nil {
						return 0, err
					}
					at.MinSleep = minSleep
				}
				if attr.ExpirationDate != nil {
					expTime, err := utils.ParseTimeDetectLayout(*attr.ExpirationDate, self.Config.DefaultTimezone)
					if err != nil {
						return 0, err
					}
					at.ExpirationDate = expTime
				}
				if attr.ActivationDate != nil {
					actTime, err := utils.ParseTimeDetectLayout(*attr.ActivationDate, self.Config.DefaultTimezone)
					if err != nil {
						return 0, err
					}
					at.ActivationDate = actTime
				}
				at.Balance = &engine.BalanceFilter{}
				if attr.BalanceID != nil {
					at.Balance.ID = attr.BalanceID
				}
				if attr.BalanceType != nil {
					at.Balance.Type = attr.BalanceType
				}
				if attr.BalanceDirections != nil {
					at.Balance.Directions = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDirections...))
				}
				if attr.BalanceDestinationIds != nil {
					at.Balance.DestinationIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDestinationIds...))
				}
				if attr.BalanceWeight != nil {
					at.Balance.Weight = attr.BalanceWeight
				}
				if attr.BalanceExpirationDate != nil {
					balanceExpTime, err := utils.ParseDate(*attr.BalanceExpirationDate)
					if err != nil {
						return 0, err
					}
					at.Balance.ExpirationDate = &balanceExpTime
				}
				if attr.BalanceTimingTags != nil {
					at.Balance.TimingIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceTimingTags...))
				}
				if attr.BalanceRatingSubject != nil {
					at.Balance.RatingSubject = attr.BalanceRatingSubject
				}
				if attr.BalanceCategories != nil {
					at.Balance.Categories = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceCategories...))
				}
				if attr.BalanceSharedGroups != nil {
					at.Balance.SharedGroups = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceSharedGroups...))
				}
				if attr.BalanceBlocker != nil {
					at.Balance.Blocker = attr.BalanceBlocker
				}
				if attr.BalanceDisabled != nil {
					at.Balance.Disabled = attr.BalanceDisabled
				}
				if attr.MinQueuedItems != nil {
					at.MinQueuedItems = *attr.MinQueuedItems
				}
				if attr.ActionsID != nil {
					at.ActionsID = *attr.ActionsID
				}
			}

		}
		account.ExecuteActionTriggers(nil)
		if err := self.AccountDb.SetAccount(account); err != nil {
			return 0, err
		}
		return 0, nil
	}, 0, accID)
	if err != nil {
		*reply = err.Error()
		return err
	}
	*reply = utils.OK
	return nil
}
コード例 #23
0
func TestTutLocalLcrQosThreshold(t *testing.T) {
	if !*testLocal {
		return
	}
	tStart, _ := utils.ParseDate("2014-08-04T13:00:00Z")
	tEnd, _ := utils.ParseDate("2014-08-04T13:01:00Z")
	cd := engine.CallDescriptor{
		Direction:   "*out",
		Category:    "call",
		Tenant:      "cgrates.org",
		Subject:     "1003",
		Account:     "1003",
		Destination: "1002",
		TimeStart:   tStart,
		TimeEnd:     tEnd,
	}
	eLcr := &engine.LCRCost{
		Entry: &engine.LCREntry{DestinationId: "DST_1002", RPCategory: "lcr_profile1", Strategy: engine.LCR_STRATEGY_QOS_THRESHOLD, StrategyParams: "20;;;;2m;;;;;;;", Weight: 10.0},
		SupplierCosts: []*engine.LCRSupplierCost{
			&engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl2", Cost: 0.6, Duration: 60 * time.Second,
				QOS: map[string]float64{engine.TCD: 270, engine.ACC: 0.3625, engine.TCC: 0.725, engine.ASR: 100, engine.ACD: 135}},
			&engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl1", Cost: 1.2, Duration: 60 * time.Second,
				QOS: map[string]float64{engine.TCD: 240, engine.ACC: 0.35, engine.TCC: 0.7, engine.ASR: 100, engine.ACD: 120}},
		},
	}
	var lcr engine.LCRCost
	if err := tutLocalRpc.Call("Responder.GetLCR", cd, &lcr); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(eLcr.Entry, lcr.Entry) {
		t.Errorf("Expecting: %+v, received: %+v", eLcr.Entry, lcr.Entry)
		//} else if !reflect.DeepEqual(eLcr.SupplierCosts, lcr.SupplierCosts) {
		//	t.Errorf("Expecting: %+v, %+v received: %+v, %+v", eLcr.SupplierCosts[0], eLcr.SupplierCosts[1], lcr.SupplierCosts[0], lcr.SupplierCosts[1])
	}
	testCdr4 := &engine.StoredCdr{CgrId: utils.Sha1("testcdr4", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()),
		TOR: utils.VOICE, AccId: "testcdr4", CdrHost: "192.168.1.1", CdrSource: "TEST_QOS_LCR", ReqType: utils.META_RATED,
		Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1003", Subject: "1003", Destination: "1004",
		SetupTime: time.Date(2014, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2014, 12, 7, 8, 42, 26, 0, time.UTC),
		Usage: time.Duration(60) * time.Second, Supplier: "suppl2"}
	var reply string
	if err := tutLocalRpc.Call("CdrsV2.ProcessCdr", testCdr4, &reply); err != nil { // Should drop ACD under the 2m required by threshold,  removing suppl2 from lcr
		t.Error("Unexpected error: ", err.Error())
	} else if reply != utils.OK {
		t.Error("Unexpected reply received: ", reply)
	}
	eLcr = &engine.LCRCost{
		Entry: &engine.LCREntry{DestinationId: "DST_1002", RPCategory: "lcr_profile1", Strategy: engine.LCR_STRATEGY_QOS_THRESHOLD, StrategyParams: "20;;;;2m;;;;;;;", Weight: 10.0},
		SupplierCosts: []*engine.LCRSupplierCost{
			&engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl1", Cost: 1.2, Duration: 60 * time.Second,
				QOS: map[string]float64{engine.TCD: 240, engine.ACC: 0.35, engine.TCC: 0.7, engine.ASR: 100, engine.ACD: 120}},
		},
	}
	if err := tutLocalRpc.Call("Responder.GetLCR", cd, &lcr); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(eLcr.Entry, lcr.Entry) {
		t.Errorf("Expecting: %+v, received: %+v", eLcr.Entry, lcr.Entry)
		//} else if !reflect.DeepEqual(eLcr.SupplierCosts, lcr.SupplierCosts) {
		//	t.Errorf("Expecting: %+v, received: %+v", eLcr.SupplierCosts[0], lcr.SupplierCosts[0])
	}
	cd = engine.CallDescriptor{
		Direction:   "*out",
		Category:    "call",
		Tenant:      "cgrates.org",
		Subject:     "1003",
		Account:     "1003",
		Destination: "1004",
		TimeStart:   tStart,
		TimeEnd:     tEnd,
	}
	eLcr = &engine.LCRCost{
		Entry: &engine.LCREntry{DestinationId: utils.ANY, RPCategory: "lcr_profile1", Strategy: engine.LCR_STRATEGY_QOS_THRESHOLD, StrategyParams: "40;;;;90s;;;;;;;", Weight: 10.0},
		SupplierCosts: []*engine.LCRSupplierCost{
			&engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl1", Cost: 1.2, Duration: 60 * time.Second,
				QOS: map[string]float64{engine.TCD: 240, engine.ACC: 0.35, engine.TCC: 0.7, engine.ASR: 100, engine.ACD: 120}},
			&engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl2", Cost: 1.2, Duration: 60 * time.Second,
				QOS: map[string]float64{engine.TCD: 330, engine.ACC: 0.3416666667, engine.TCC: 1.025, engine.ASR: 100, engine.ACD: 110}},
		},
	}
	/*eLcr2 := &engine.LCRCost{
		Entry: &engine.LCREntry{DestinationId: utils.ANY, RPCategory: "lcr_profile1", Strategy: engine.LCR_STRATEGY_QOS_THRESHOLD, StrategyParams: "40;;90s;;;;;;;", Weight: 10.0},
		SupplierCosts: []*engine.LCRSupplierCost{
			&engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl2", Cost: 1.2, Duration: 60 * time.Second,
				QOS: map[string]float64{engine.TCD: 330, engine.ACC: 0.3416666667, engine.TCC: 1.025, engine.ASR: 100, engine.ACD: 110}},
			&engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl1", Cost: 1.2, Duration: 60 * time.Second,
				QOS: map[string]float64{engine.TCD: 240, engine.ACC: 0.35, engine.TCC: 0.7, engine.ASR: 100, engine.ACD: 120}},
		},
	}
	*/
	if err := tutLocalRpc.Call("Responder.GetLCR", cd, &lcr); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(eLcr.Entry, lcr.Entry) {
		t.Errorf("Expecting: %+v, received: %+v", eLcr.Entry, lcr.Entry)
		//} else if !reflect.DeepEqual(eLcr.SupplierCosts, lcr.SupplierCosts) && !reflect.DeepEqual(eLcr2.SupplierCosts, lcr.SupplierCosts) {
		//	t.Errorf("Expecting: %+v, received: %+v", eLcr.SupplierCosts[1], lcr.SupplierCosts[1])
	}
	testCdr5 := &engine.StoredCdr{CgrId: utils.Sha1("testcdr5", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()),
		TOR: utils.VOICE, AccId: "testcdr5", CdrHost: "192.168.1.1", CdrSource: "TEST_QOS_LCR", ReqType: utils.META_RATED,
		Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1003", Subject: "1003", Destination: "1004",
		SetupTime: time.Date(2014, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2014, 12, 7, 8, 42, 26, 0, time.UTC),
		Usage: time.Duration(1) * time.Second, Supplier: "suppl2"}
	if err := tutLocalRpc.Call("CdrsV2.ProcessCdr", testCdr5, &reply); err != nil { // Should drop ACD under the 1m required by threshold,  removing suppl2 from lcr
		t.Error("Unexpected error: ", err.Error())
	} else if reply != utils.OK {
		t.Error("Unexpected reply received: ", reply)
	}
	eLcr = &engine.LCRCost{
		Entry: &engine.LCREntry{DestinationId: utils.ANY, RPCategory: "lcr_profile1", Strategy: engine.LCR_STRATEGY_QOS_THRESHOLD, StrategyParams: "40;;;;90s;;;;;;;", Weight: 10.0},
		SupplierCosts: []*engine.LCRSupplierCost{
			&engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl1", Cost: 1.2, Duration: 60 * time.Second,
				QOS: map[string]float64{engine.TCD: 240, engine.ACC: 0.35, engine.TCC: 0.7, engine.ASR: 100, engine.ACD: 120}},
		},
	}
	if err := tutLocalRpc.Call("Responder.GetLCR", cd, &lcr); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(eLcr.Entry, lcr.Entry) {
		t.Errorf("Expecting: %+v, received: %+v", eLcr.Entry, lcr.Entry)
		//} else if !reflect.DeepEqual(eLcr.SupplierCosts, lcr.SupplierCosts) {
		//	t.Errorf("Expecting: %+v, received: %+v", eLcr.SupplierCosts[0], lcr.SupplierCosts[0])
	}
}
コード例 #24
0
func TestTutLocalLcrQos(t *testing.T) {
	if !*testLocal {
		return
	}
	tStart, _ := utils.ParseDate("2014-08-04T13:00:00Z")
	tEnd, _ := utils.ParseDate("2014-08-04T13:01:00Z")
	cd := engine.CallDescriptor{
		Direction:   "*out",
		Category:    "call",
		Tenant:      "cgrates.org",
		Subject:     "1002",
		Account:     "1002",
		Destination: "1003",
		TimeStart:   tStart,
		TimeEnd:     tEnd,
	}
	eStLcr := &engine.LCRCost{
		Entry: &engine.LCREntry{DestinationId: utils.ANY, RPCategory: "lcr_profile1", Strategy: engine.LCR_STRATEGY_QOS, StrategyParams: "", Weight: 10.0},
		SupplierCosts: []*engine.LCRSupplierCost{
			&engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl1", Cost: 1.2, Duration: 60 * time.Second,
				QOS: map[string]float64{engine.TCD: -1, engine.ACC: -1, engine.TCC: -1, engine.ASR: -1, engine.ACD: -1, engine.DDC: -1}},
			&engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl2", Cost: 1.2, Duration: 60 * time.Second,
				QOS: map[string]float64{engine.TCD: -1, engine.ACC: -1, engine.TCC: -1, engine.ASR: -1, engine.ACD: -1, engine.DDC: -1}},
		},
	}
	eStLcr2 := &engine.LCRCost{
		Entry: &engine.LCREntry{DestinationId: utils.ANY, RPCategory: "lcr_profile1", Strategy: engine.LCR_STRATEGY_QOS, StrategyParams: "", Weight: 10.0},
		SupplierCosts: []*engine.LCRSupplierCost{
			&engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl2", Cost: 1.2, Duration: 60 * time.Second,
				QOS: map[string]float64{engine.TCD: -1, engine.ACC: -1, engine.TCC: -1, engine.ASR: -1, engine.ACD: -1, engine.DDC: -1}},
			&engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl1", Cost: 1.2, Duration: 60 * time.Second,
				QOS: map[string]float64{engine.TCD: -1, engine.ACC: -1, engine.TCC: -1, engine.ASR: -1, engine.ACD: -1, engine.DDC: -1}},
		},
	}
	var lcr engine.LCRCost
	// Since there is no real quality difference, the suppliers will come in random order here
	if err := tutLocalRpc.Call("Responder.GetLCR", cd, &lcr); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(eStLcr.Entry, lcr.Entry) {
		t.Errorf("Expecting: %+v, received: %+v", eStLcr.Entry, lcr.Entry)
	} else if !reflect.DeepEqual(eStLcr.SupplierCosts, lcr.SupplierCosts) && !reflect.DeepEqual(eStLcr2.SupplierCosts, lcr.SupplierCosts) {
		t.Errorf("Expecting: %+v, received: %+v", eStLcr.SupplierCosts[0], lcr.SupplierCosts[0])
	}
	// Post some CDRs to influence stats
	testCdr1 := &engine.StoredCdr{CgrId: utils.Sha1("testcdr1", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()),
		TOR: utils.VOICE, AccId: "testcdr1", CdrHost: "192.168.1.1", CdrSource: "TEST_QOS_LCR", ReqType: utils.META_RATED,
		Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
		SetupTime: time.Date(2014, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2014, 12, 7, 8, 42, 26, 0, time.UTC),
		Usage: time.Duration(2) * time.Minute, Supplier: "suppl1",
		ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}}
	testCdr2 := &engine.StoredCdr{CgrId: utils.Sha1("testcdr2", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()),
		TOR: utils.VOICE, AccId: "testcdr2", CdrHost: "192.168.1.1", CdrSource: "TEST_QOS_LCR", ReqType: utils.META_RATED,
		Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1002", Subject: "1002", Destination: "1003",
		SetupTime: time.Date(2014, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2014, 12, 7, 8, 42, 26, 0, time.UTC),
		Usage: time.Duration(90) * time.Second, Supplier: "suppl2",
		ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}}
	var reply string
	for _, cdr := range []*engine.StoredCdr{testCdr1, testCdr2} {
		if err := tutLocalRpc.Call("CdrsV2.ProcessCdr", cdr, &reply); err != nil {
			t.Error("Unexpected error: ", err.Error())
		} else if reply != utils.OK {
			t.Error("Unexpected reply received: ", reply)
		}
	}
	// Based on stats, supplier1 should always be better since he has a higer ACD
	eStLcr = &engine.LCRCost{
		Entry: &engine.LCREntry{DestinationId: utils.ANY, RPCategory: "lcr_profile1", Strategy: engine.LCR_STRATEGY_QOS, StrategyParams: "", Weight: 10.0},
		SupplierCosts: []*engine.LCRSupplierCost{
			&engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl1", Cost: 1.2, Duration: 60 * time.Second,
				QOS: map[string]float64{engine.TCD: 240, engine.ACC: 0.35, engine.TCC: 0.7, engine.ASR: 100, engine.ACD: 120}},
			&engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl2", Cost: 1.2, Duration: 60 * time.Second,
				QOS: map[string]float64{engine.TCD: 90, engine.ACC: 0.325, engine.TCC: 0.325, engine.ASR: 100, engine.ACD: 90}},
		},
	}
	if err := tutLocalRpc.Call("Responder.GetLCR", cd, &lcr); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(eStLcr.Entry, lcr.Entry) {
		t.Errorf("Expecting: %+v, received: %+v", eStLcr.Entry, lcr.Entry)
		//} else if !reflect.DeepEqual(eStLcr.SupplierCosts, lcr.SupplierCosts) && !reflect.DeepEqual(eStLcr2.SupplierCosts, lcr.SupplierCosts) {
		//	t.Errorf("Expecting: %+v, %+v, received: %+v, %+v", eStLcr.SupplierCosts[0], eStLcr.SupplierCosts[1], lcr.SupplierCosts[0], lcr.SupplierCosts[1])
	}
	testCdr3 := &engine.StoredCdr{CgrId: utils.Sha1("testcdr3", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()),
		TOR: utils.VOICE, AccId: "testcdr3", CdrHost: "192.168.1.1", CdrSource: "TEST_QOS_LCR", ReqType: utils.META_RATED,
		Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1003", Subject: "1003", Destination: "1004",
		SetupTime: time.Date(2014, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2014, 12, 7, 8, 42, 26, 0, time.UTC),
		Usage: time.Duration(180) * time.Second, Supplier: "suppl2"}
	if err := tutLocalRpc.Call("CdrsV2.ProcessCdr", testCdr3, &reply); err != nil {
		t.Error("Unexpected error: ", err.Error())
	} else if reply != utils.OK {
		t.Error("Unexpected reply received: ", reply)
	}
	// Since ACD has considerably increased for supplier2, we should have it as first prio now
	eStLcr = &engine.LCRCost{
		Entry: &engine.LCREntry{DestinationId: utils.ANY, RPCategory: "lcr_profile1", Strategy: engine.LCR_STRATEGY_QOS, StrategyParams: "", Weight: 10.0},
		SupplierCosts: []*engine.LCRSupplierCost{
			&engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl2", Cost: 1.2, Duration: 60 * time.Second,
				QOS: map[string]float64{engine.TCD: 270, engine.ACC: 0.3625, engine.TCC: 0.725, engine.ASR: 100, engine.ACD: 135}},
			&engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl1", Cost: 1.2, Duration: 60 * time.Second,
				QOS: map[string]float64{engine.TCD: 240, engine.ACC: 0.35, engine.TCC: 0.7, engine.ASR: 100, engine.ACD: 120}},
		},
	}
	if err := tutLocalRpc.Call("Responder.GetLCR", cd, &lcr); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(eStLcr.Entry, lcr.Entry) {
		t.Errorf("Expecting: %+v, received: %+v", eStLcr.Entry, lcr.Entry)
		//} else if !reflect.DeepEqual(eStLcr.SupplierCosts, lcr.SupplierCosts) && !reflect.DeepEqual(eStLcr2.SupplierCosts, lcr.SupplierCosts) {
		//	t.Errorf("Expecting: %+v, %+v, received: %+v, %+v", eStLcr.SupplierCosts[0], eStLcr.SupplierCosts[1], lcr.SupplierCosts[0], lcr.SupplierCosts[1])
	}
}
コード例 #25
0
ファイル: tp_reader.go プロジェクト: foehn/cgrates
func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error {
	accountActions, err := tpr.lr.GetTpAccountActions(qriedAA)
	if err != nil {
		return errors.New(err.Error() + ": " + fmt.Sprintf("%+v", qriedAA))
	}
	storAas, err := TpAccountActions(accountActions).GetAccountActions()
	if err != nil {
		return err
	}
	for _, accountAction := range storAas {
		id := accountAction.KeyId()
		var actionsIds []string // collects action ids
		// action timings
		if accountAction.ActionPlanId != "" {
			// get old userBalanceIds
			var exitingAccountIds []string
			existingActionPlans, err := tpr.ratingStorage.GetActionPlans(accountAction.ActionPlanId)
			if err == nil && len(existingActionPlans) > 0 {
				// all action timings from a specific tag shuld have the same list of user balances from the first one
				exitingAccountIds = existingActionPlans[0].AccountIds
			}

			tpap, err := tpr.lr.GetTpActionPlans(tpr.tpid, accountAction.ActionPlanId)
			if err != nil {
				return errors.New(err.Error() + " (ActionPlan): " + accountAction.ActionPlanId)
			} else if len(tpap) == 0 {
				return fmt.Errorf("no action plan with id <%s>", accountAction.ActionPlanId)
			}
			aps, err := TpActionPlans(tpap).GetActionPlans()
			if err != nil {
				return err
			}
			var actionTimings []*ActionPlan
			ats := aps[accountAction.ActionPlanId]
			for _, at := range ats {
				// Check action exists before saving it inside actionTiming key
				// ToDo: try saving the key after the actions was retrieved in order to save one query here.
				if actions, err := tpr.lr.GetTpActions(tpr.tpid, at.ActionsId); err != nil {
					return errors.New(err.Error() + " (Actions): " + at.ActionsId)
				} else if len(actions) == 0 {
					return fmt.Errorf("no action with id <%s>", at.ActionsId)
				}
				var t *utils.TPTiming
				if at.TimingId != utils.ASAP {
					tptm, err := tpr.lr.GetTpTimings(tpr.tpid, at.TimingId)
					if err != nil {
						return errors.New(err.Error() + " (Timing): " + at.TimingId)
					} else if len(tptm) == 0 {
						return fmt.Errorf("no timing with id <%s>", at.TimingId)
					}
					tm, err := TpTimings(tptm).GetTimings()
					if err != nil {
						return err
					}
					t = tm[at.TimingId]
				} else {
					t = tpr.timings[at.TimingId] // *asap
				}
				actPln := &ActionPlan{
					Uuid:   utils.GenUUID(),
					Id:     accountAction.ActionPlanId,
					Weight: at.Weight,
					Timing: &RateInterval{
						Timing: &RITiming{
							Months:    t.Months,
							MonthDays: t.MonthDays,
							WeekDays:  t.WeekDays,
							StartTime: t.StartTime,
						},
					},
					ActionsId: at.ActionsId,
				}
				// collect action ids from timings
				actionsIds = append(actionsIds, actPln.ActionsId)
				//add user balance id if no already in
				found := false
				for _, ubId := range exitingAccountIds {
					if ubId == id {
						found = true
						break
					}
				}
				if !found {
					actPln.AccountIds = append(exitingAccountIds, id)
				}
				actionTimings = append(actionTimings, actPln)
			}

			// write action triggers
			err = tpr.ratingStorage.SetActionPlans(accountAction.ActionPlanId, actionTimings)
			if err != nil {
				return errors.New(err.Error() + " (SetActionPlan): " + accountAction.ActionPlanId)
			}
		}
		// action triggers
		var actionTriggers ActionTriggers
		//ActionTriggerPriotityList []*ActionTrigger
		if accountAction.ActionTriggersId != "" {
			tpatrs, err := tpr.lr.GetTpActionTriggers(tpr.tpid, accountAction.ActionTriggersId)
			if err != nil {
				return errors.New(err.Error() + " (ActionTriggers): " + accountAction.ActionTriggersId)
			}
			atrs, err := TpActionTriggers(tpatrs).GetActionTriggers()
			if err != nil {
				return err
			}

			atrsMap := make(map[string][]*ActionTrigger)
			for key, atrsLst := range atrs {
				atrs := make([]*ActionTrigger, len(atrsLst))
				for idx, apiAtr := range atrsLst {
					minSleep, _ := utils.ParseDurationWithSecs(apiAtr.MinSleep)
					expTime, _ := utils.ParseDate(apiAtr.BalanceExpirationDate)
					atrs[idx] = &ActionTrigger{
						ThresholdType:         apiAtr.ThresholdType,
						ThresholdValue:        apiAtr.ThresholdValue,
						Recurrent:             apiAtr.Recurrent,
						MinSleep:              minSleep,
						BalanceId:             apiAtr.BalanceId,
						BalanceType:           apiAtr.BalanceType,
						BalanceDirection:      apiAtr.BalanceDirection,
						BalanceDestinationIds: apiAtr.BalanceDestinationIds,
						BalanceWeight:         apiAtr.BalanceWeight,
						BalanceExpirationDate: expTime,
						BalanceRatingSubject:  apiAtr.BalanceRatingSubject,
						BalanceCategory:       apiAtr.BalanceCategory,
						BalanceSharedGroup:    apiAtr.BalanceSharedGroup,
						Weight:                apiAtr.Weight,
						ActionsId:             apiAtr.ActionsId,
					}
				}
				atrsMap[key] = atrs
			}
			actionTriggers = atrsMap[accountAction.ActionTriggersId]
			// collect action ids from triggers
			for _, atr := range actionTriggers {
				actionsIds = append(actionsIds, atr.ActionsId)
			}
			// write action triggers
			err = tpr.ratingStorage.SetActionTriggers(accountAction.ActionTriggersId, actionTriggers)
			if err != nil {
				return errors.New(err.Error() + " (SetActionTriggers): " + accountAction.ActionTriggersId)
			}
		}

		// actions
		acts := make(map[string][]*Action)
		for _, actId := range actionsIds {
			tpas, err := tpr.lr.GetTpActions(tpr.tpid, actId)
			if err != nil {
				return err
			}
			as, err := TpActions(tpas).GetActions()
			if err != nil {
				return err
			}
			for tag, tpacts := range as {
				enacts := make([]*Action, len(tpacts))
				for idx, tpact := range tpacts {
					enacts[idx] = &Action{
						Id:               tag + strconv.Itoa(idx),
						ActionType:       tpact.Identifier,
						BalanceType:      tpact.BalanceType,
						Direction:        tpact.Direction,
						Weight:           tpact.Weight,
						ExtraParameters:  tpact.ExtraParameters,
						ExpirationString: tpact.ExpiryTime,
						Balance: &Balance{
							Value:          tpact.Units,
							Weight:         tpact.BalanceWeight,
							RatingSubject:  tpact.RatingSubject,
							DestinationIds: tpact.DestinationIds,
							SharedGroup:    tpact.SharedGroup,
						},
					}
				}
				acts[tag] = enacts
			}
		}
		// write actions
		for k, as := range acts {
			err = tpr.ratingStorage.SetActions(k, as)
			if err != nil {
				return err
			}
		}
		ub, err := tpr.accountingStorage.GetAccount(id)
		if err != nil {
			ub = &Account{
				Id: id,
			}
		}
		ub.ActionTriggers = actionTriggers.Clone()

		if err := tpr.accountingStorage.SetAccount(ub); err != nil {
			return err
		}
	}
	return nil
}
コード例 #26
0
// Check call costs
func TestTutLocalGetCosts(t *testing.T) {
	if !*testLocal {
		return
	}
	tStart, _ := utils.ParseDate("2014-08-04T13:00:00Z")
	tEnd, _ := utils.ParseDate("2014-08-04T13:00:20Z")
	cd := engine.CallDescriptor{
		Direction:     "*out",
		Category:      "call",
		Tenant:        "cgrates.org",
		Subject:       "1001",
		Account:       "1001",
		Destination:   "1002",
		DurationIndex: 0,
		TimeStart:     tStart,
		TimeEnd:       tEnd,
	}
	var cc engine.CallCost
	if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil {
		t.Error("Got error on Responder.GetCost: ", err.Error())
	} else if cc.Cost != 0.6 {
		t.Errorf("Calling Responder.GetCost got callcost: %v", cc.Cost)
	}
	// Make sure that the same cost is returned via users aliasing
	cd = engine.CallDescriptor{
		Direction:     "*out",
		Category:      "call",
		Tenant:        utils.USERS,
		Subject:       utils.USERS,
		Account:       utils.USERS,
		Destination:   "1002",
		DurationIndex: 0,
		TimeStart:     tStart,
		TimeEnd:       tEnd,
		ExtraFields:   map[string]string{"Uuid": "388539dfd4f5cefee8f488b78c6c244b9e19138e"},
	}
	if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil {
		t.Error("Got error on Responder.GetCost: ", err.Error())
	} else if cc.Cost != 0.6 {
		t.Errorf("Calling Responder.GetCost got callcost: %v", cc.Cost)
	}
	tStart, _ = utils.ParseDate("2014-08-04T13:00:00Z")
	tEnd, _ = utils.ParseDate("2014-08-04T13:01:25Z")
	cd = engine.CallDescriptor{
		Direction:     "*out",
		Category:      "call",
		Tenant:        "cgrates.org",
		Subject:       "1001",
		Account:       "1001",
		Destination:   "1002",
		DurationIndex: 0,
		TimeStart:     tStart,
		TimeEnd:       tEnd,
	}
	if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil {
		t.Error("Got error on Responder.GetCost: ", err.Error())
	} else if cc.Cost != 0.6417 { // 0.01 first minute, 0.04 25 seconds with RT_20CNT
		t.Errorf("Calling Responder.GetCost got callcost: %v", cc.Cost)
	}
	tStart, _ = utils.ParseDate("2014-08-04T13:00:00Z")
	tEnd, _ = utils.ParseDate("2014-08-04T13:00:20Z")
	cd = engine.CallDescriptor{
		Direction:     "*out",
		Category:      "call",
		Tenant:        "cgrates.org",
		Subject:       "1001",
		Account:       "1001",
		Destination:   "1003",
		DurationIndex: 0,
		TimeStart:     tStart,
		TimeEnd:       tEnd,
	}
	if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil {
		t.Error("Got error on Responder.GetCost: ", err.Error())
	} else if cc.Cost != 1 {
		t.Errorf("Calling Responder.GetCost got callcost: %v", cc.Cost)
	}
	tStart, _ = utils.ParseDate("2014-08-04T13:00:00Z")
	tEnd, _ = utils.ParseDate("2014-08-04T13:01:25Z")
	cd = engine.CallDescriptor{
		Direction:     "*out",
		Category:      "call",
		Tenant:        "cgrates.org",
		Subject:       "1001",
		Account:       "1001",
		Destination:   "1003",
		DurationIndex: 0,
		TimeStart:     tStart,
		TimeEnd:       tEnd,
	}
	if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil {
		t.Error("Got error on Responder.GetCost: ", err.Error())
	} else if cc.Cost != 1.3 {
		t.Errorf("Calling Responder.GetCost got callcost: %v", cc.Cost)
	}
	tStart, _ = utils.ParseDate("2014-08-04T13:00:00Z")
	tEnd, _ = utils.ParseDate("2014-08-04T13:00:20Z")
	cd = engine.CallDescriptor{
		Direction:     "*out",
		Category:      "call",
		Tenant:        "cgrates.org",
		Subject:       "1001",
		Account:       "1001",
		Destination:   "1004",
		DurationIndex: 0,
		TimeStart:     tStart,
		TimeEnd:       tEnd,
	}
	if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil {
		t.Error("Got error on Responder.GetCost: ", err.Error())
	} else if cc.Cost != 1 {
		t.Errorf("Calling Responder.GetCost got callcost: %v", cc.Cost)
	}
	tStart, _ = utils.ParseDate("2014-08-04T13:00:00Z")
	tEnd, _ = utils.ParseDate("2014-08-04T13:01:25Z")
	cd = engine.CallDescriptor{
		Direction:     "*out",
		Category:      "call",
		Tenant:        "cgrates.org",
		Subject:       "1001",
		Account:       "1001",
		Destination:   "1004",
		DurationIndex: 0,
		TimeStart:     tStart,
		TimeEnd:       tEnd,
	}
	if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil {
		t.Error("Got error on Responder.GetCost: ", err.Error())
	} else if cc.Cost != 1.3 {
		t.Errorf("Calling Responder.GetCost got callcost: %v", cc.Cost)
	}
	tStart = time.Date(2014, 8, 4, 13, 0, 0, 0, time.UTC)
	cd = engine.CallDescriptor{
		Direction:   "*out",
		Category:    "call",
		Tenant:      "cgrates.org",
		Subject:     "1001",
		Account:     "1001",
		Destination: "1007",
		TimeStart:   tStart,
		TimeEnd:     tStart.Add(time.Duration(50) * time.Second),
	}
	if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil {
		t.Error("Got error on Responder.GetCost: ", err.Error())
	} else if cc.Cost != 0.5 {
		t.Errorf("Calling Responder.GetCost got callcost: %s", cc.AsJSON())
	}
	cd = engine.CallDescriptor{
		Direction:   "*out",
		Category:    "call",
		Tenant:      "cgrates.org",
		Subject:     "1001",
		Account:     "1001",
		Destination: "1007",
		TimeStart:   tStart,
		TimeEnd:     tStart.Add(time.Duration(70) * time.Second),
	}
	if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil {
		t.Error("Got error on Responder.GetCost: ", err.Error())
	} else if cc.Cost != 0.62 {
		t.Errorf("Calling Responder.GetCost got callcost: %v", cc.Cost)
	}
	cd = engine.CallDescriptor{
		Direction:   "*out",
		Category:    "call",
		Tenant:      "cgrates.org",
		Subject:     "1002",
		Account:     "1002",
		Destination: "1007",
		TimeStart:   tStart,
		TimeEnd:     tStart.Add(time.Duration(50) * time.Second),
	}
	if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil {
		t.Error("Got error on Responder.GetCost: ", err.Error())
	} else if cc.Cost != 0.5 {
		t.Errorf("Calling Responder.GetCost got callcost: %s", cc.AsJSON())
	}
	cd = engine.CallDescriptor{
		Direction:   "*out",
		Category:    "call",
		Tenant:      "cgrates.org",
		Subject:     "1002",
		Account:     "1002",
		Destination: "1007",
		TimeStart:   tStart,
		TimeEnd:     tStart.Add(time.Duration(70) * time.Second),
	}
	if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil {
		t.Error("Got error on Responder.GetCost: ", err.Error())
	} else if cc.Cost != 0.7 { // In case of *disconnect strategy, it will not be applied so we can go on negative costs
		t.Errorf("Calling Responder.GetCost got callcost: %s", cc.AsJSON())
	}
}
コード例 #27
0
ファイル: triggers.go プロジェクト: rinor/cgrates
func (attr *AttrSetAccountActionTriggers) UpdateActionTrigger(at *engine.ActionTrigger, timezone string) (updated bool, err error) {
	if at == nil {
		return false, errors.New("Empty ActionTrigger")
	}
	if at.ID == "" { // New AT, update it's data
		if missing := utils.MissingStructFields(attr, []string{"GroupID", "ThresholdType", "ThresholdValue"}); len(missing) != 0 {
			return false, utils.NewErrMandatoryIeMissing(missing...)
		}
		at.ID = *attr.GroupID
		if attr.UniqueID != nil {
			at.UniqueID = *attr.UniqueID
		}
	}
	if attr.GroupID != nil && *attr.GroupID != at.ID {
		return
	}
	if attr.UniqueID != nil && *attr.UniqueID != at.UniqueID {
		return
	}
	// at matches
	updated = true
	if attr.ThresholdType != nil {
		at.ThresholdType = *attr.ThresholdType
	}
	if attr.ThresholdValue != nil {
		at.ThresholdValue = *attr.ThresholdValue
	}
	if attr.Recurrent != nil {
		at.Recurrent = *attr.Recurrent
	}
	if attr.Executed != nil {
		at.Executed = *attr.Executed
	}
	if attr.MinSleep != nil {
		if at.MinSleep, err = utils.ParseDurationWithSecs(*attr.MinSleep); err != nil {
			return
		}
	}
	if attr.ExpirationDate != nil {
		if at.ExpirationDate, err = utils.ParseTimeDetectLayout(*attr.ExpirationDate, timezone); err != nil {
			return
		}
	}
	if attr.ActivationDate != nil {
		if at.ActivationDate, err = utils.ParseTimeDetectLayout(*attr.ActivationDate, timezone); err != nil {
			return
		}
	}
	if at.Balance == nil {
		at.Balance = &engine.BalanceFilter{}
	}
	if attr.BalanceID != nil {
		at.Balance.ID = attr.BalanceID
	}
	if attr.BalanceType != nil {
		at.Balance.Type = attr.BalanceType
	}
	if attr.BalanceDirections != nil {
		at.Balance.Directions = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDirections...))
	}
	if attr.BalanceDestinationIds != nil {
		at.Balance.DestinationIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDestinationIds...))
	}
	if attr.BalanceWeight != nil {
		at.Balance.Weight = attr.BalanceWeight
	}
	if attr.BalanceExpirationDate != nil {
		balanceExpTime, err := utils.ParseDate(*attr.BalanceExpirationDate)
		if err != nil {
			return false, err
		}
		at.Balance.ExpirationDate = &balanceExpTime
	}
	if attr.BalanceTimingTags != nil {
		at.Balance.TimingIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceTimingTags...))
	}
	if attr.BalanceRatingSubject != nil {
		at.Balance.RatingSubject = attr.BalanceRatingSubject
	}
	if attr.BalanceCategories != nil {
		at.Balance.Categories = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceCategories...))
	}
	if attr.BalanceSharedGroups != nil {
		at.Balance.SharedGroups = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceSharedGroups...))
	}
	if attr.BalanceBlocker != nil {
		at.Balance.Blocker = attr.BalanceBlocker
	}
	if attr.BalanceDisabled != nil {
		at.Balance.Disabled = attr.BalanceDisabled
	}
	if attr.MinQueuedItems != nil {
		at.MinQueuedItems = *attr.MinQueuedItems
	}
	if attr.ActionsID != nil {
		at.ActionsID = *attr.ActionsID
	}
	return
}
コード例 #28
0
func (at *ActionTrigger) Execute(ub *Account, sq *StatsQueueTriggered) (err error) {
	// check for min sleep time
	if at.Recurrent && !at.lastExecutionTime.IsZero() && time.Since(at.lastExecutionTime) < at.MinSleep {
		return
	}
	at.lastExecutionTime = time.Now()
	if ub != nil && ub.Disabled {
		return fmt.Errorf("User %s is disabled and there are triggers in action!", ub.Id)
	}
	// does NOT need to Lock() because it is triggered from a method that took the Lock
	var aac Actions
	aac, err = ratingStorage.GetActions(at.ActionsId, false)
	aac.Sort()
	if err != nil {
		utils.Logger.Err(fmt.Sprintf("Failed to get actions: %v", err))
		return
	}
	at.Executed = true
	transactionFailed := false
	toBeSaved := true
	for _, a := range aac {
		if a.Balance == nil {
			a.Balance = &Balance{}
		}
		a.Balance.ExpirationDate, _ = utils.ParseDate(a.ExpirationString)
		// handle remove action
		if a.ActionType == REMOVE_ACCOUNT {
			accId := ub.Id
			if err := accountingStorage.RemoveAccount(accId); err != nil {
				utils.Logger.Err(fmt.Sprintf("Could not remove account Id: %s: %v", accId, err))
				transactionFailed = true
				break
			}
			// clean the account id from all action plans
			allATs, err := ratingStorage.GetAllActionPlans()
			if err != nil && err != utils.ErrNotFound {
				utils.Logger.Err(fmt.Sprintf("Could not get action plans: %s: %v", accId, err))
				transactionFailed = true
				break
			}
			for key, ats := range allATs {
				changed := false
				for _, at := range ats {
					for i := 0; i < len(at.AccountIds); i++ {
						if at.AccountIds[i] == accId {
							// delete without preserving order
							at.AccountIds[i] = at.AccountIds[len(at.AccountIds)-1]
							at.AccountIds = at.AccountIds[:len(at.AccountIds)-1]
							i -= 1
							changed = true
						}
					}
				}
				if changed {
					// save action plan
					ratingStorage.SetActionPlans(key, ats)
					// cache
					ratingStorage.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: []string{utils.ACTION_PLAN_PREFIX + key}})
				}
			}
			toBeSaved = false
			continue // do not go to getActionFunc
			// TODO: maybe we should break here as the account is gone
			// will leave continue for now as the next action can create another acount
		}

		actionFunction, exists := getActionFunc(a.ActionType)
		if !exists {
			utils.Logger.Err(fmt.Sprintf("Function type %v not available, aborting execution!", a.ActionType))
			transactionFailed = false
			break
		}
		//go utils.Logger.Info(fmt.Sprintf("Executing %v, %v: %v", ub, sq, a))
		if err := actionFunction(ub, sq, a, aac); err != nil {
			utils.Logger.Err(fmt.Sprintf("Error executing action %s: %v!", a.ActionType, err))
			transactionFailed = false
			break
		}
		toBeSaved = true
	}
	if transactionFailed || at.Recurrent {
		at.Executed = false
	}
	if !transactionFailed && ub != nil {
		storageLogger.LogActionTrigger(ub.Id, utils.RATER_SOURCE, at, aac)
		if toBeSaved {
			accountingStorage.SetAccount(ub)
		}
	}
	return
}
コード例 #29
0
ファイル: tp_reader.go プロジェクト: foehn/cgrates
func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) {
	tps, err := tpr.lr.GetTpCdrStats(tpr.tpid, tag)
	if err != nil {
		return err
	}
	storStats, err := TpCdrStats(tps).GetCdrStats()
	if err != nil {
		return err
	}
	var actionsIds []string // collect action ids
	for tag, tpStats := range storStats {
		for _, tpStat := range tpStats {
			var cs *CdrStats
			var exists bool
			if cs, exists = tpr.cdrStats[tag]; !exists {
				cs = &CdrStats{Id: tag}
			}
			// action triggers
			triggerTag := tpStat.ActionTriggers
			if triggerTag != "" {
				_, exists := tpr.actionsTriggers[triggerTag]
				if !exists {
					tpatrs, err := tpr.lr.GetTpActionTriggers(tpr.tpid, triggerTag)
					if err != nil {
						return errors.New(err.Error() + " (ActionTriggers): " + triggerTag)
					}
					atrsM, err := TpActionTriggers(tpatrs).GetActionTriggers()
					if err != nil {
						return err
					}

					for _, atrsLst := range atrsM {
						atrs := make([]*ActionTrigger, len(atrsLst))
						for idx, apiAtr := range atrsLst {
							minSleep, _ := utils.ParseDurationWithSecs(apiAtr.MinSleep)
							expTime, _ := utils.ParseDate(apiAtr.BalanceExpirationDate)
							atrs[idx] = &ActionTrigger{
								ThresholdType:         apiAtr.ThresholdType,
								ThresholdValue:        apiAtr.ThresholdValue,
								Recurrent:             apiAtr.Recurrent,
								MinSleep:              minSleep,
								BalanceId:             apiAtr.BalanceId,
								BalanceType:           apiAtr.BalanceType,
								BalanceDirection:      apiAtr.BalanceDirection,
								BalanceDestinationIds: apiAtr.BalanceDestinationIds,
								BalanceWeight:         apiAtr.BalanceWeight,
								BalanceExpirationDate: expTime,
								BalanceRatingSubject:  apiAtr.BalanceRatingSubject,
								BalanceCategory:       apiAtr.BalanceCategory,
								BalanceSharedGroup:    apiAtr.BalanceSharedGroup,
								Weight:                apiAtr.Weight,
								ActionsId:             apiAtr.ActionsId,
							}
						}
						tpr.actionsTriggers[triggerTag] = atrs
					}
				}
				// collect action ids from triggers
				for _, atr := range tpr.actionsTriggers[triggerTag] {
					actionsIds = append(actionsIds, atr.ActionsId)
				}
			}
			triggers, exists := tpr.actionsTriggers[triggerTag]
			if triggerTag != "" && !exists {
				// only return error if there was something there for the tag
				return fmt.Errorf("could not get action triggers for cdr stats id %s: %s", cs.Id, triggerTag)
			}
			// write action triggers
			err = tpr.ratingStorage.SetActionTriggers(triggerTag, triggers)
			if err != nil {
				return errors.New(err.Error() + " (SetActionTriggers): " + triggerTag)
			}
			UpdateCdrStats(cs, triggers, tpStat, tpr.timezone)
			tpr.cdrStats[tag] = cs
		}
	}
	// actions
	for _, actId := range actionsIds {
		_, exists := tpr.actions[actId]
		if !exists {
			tpas, err := tpr.lr.GetTpActions(tpr.tpid, actId)
			if err != nil {
				return err
			}
			as, err := TpActions(tpas).GetActions()
			if err != nil {
				return err
			}
			for tag, tpacts := range as {
				enacts := make([]*Action, len(tpacts))
				for idx, tpact := range tpacts {
					enacts[idx] = &Action{
						Id:               tag + strconv.Itoa(idx),
						ActionType:       tpact.Identifier,
						BalanceType:      tpact.BalanceType,
						Direction:        tpact.Direction,
						Weight:           tpact.Weight,
						ExtraParameters:  tpact.ExtraParameters,
						ExpirationString: tpact.ExpiryTime,
						Balance: &Balance{
							Value:          tpact.Units,
							Weight:         tpact.BalanceWeight,
							RatingSubject:  tpact.RatingSubject,
							DestinationIds: tpact.DestinationIds,
							SharedGroup:    tpact.SharedGroup,
						},
					}
				}
				tpr.actions[tag] = enacts
			}
		}
	}

	if save {
		// write actions
		for k, as := range tpr.actions {
			err = tpr.ratingStorage.SetActions(k, as)
			if err != nil {
				return err
			}
		}
		for _, stat := range tpr.cdrStats {
			if err := tpr.ratingStorage.SetCdrStats(stat); err != nil {
				return err
			}
		}
	}
	return nil
}
コード例 #30
0
ファイル: tp_reader.go プロジェクト: bhepp/cgrates
func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) {
	tps, err := tpr.lr.GetTpCdrStats(tpr.tpid, tag)
	if err != nil {
		return err
	}
	storStats, err := TpCdrStats(tps).GetCdrStats()
	if err != nil {
		return err
	}
	var actionsIds []string // collect action ids
	for tag, tpStats := range storStats {
		for _, tpStat := range tpStats {
			var cs *CdrStats
			var exists bool
			if cs, exists = tpr.cdrStats[tag]; !exists {
				cs = &CdrStats{Id: tag}
			}
			// action triggers
			triggerTag := tpStat.ActionTriggers
			if triggerTag != "" {
				_, exists := tpr.actionsTriggers[triggerTag]
				if !exists {
					tpatrs, err := tpr.lr.GetTpActionTriggers(tpr.tpid, triggerTag)
					if err != nil {
						return errors.New(err.Error() + " (ActionTriggers): " + triggerTag)
					}
					atrsM, err := TpActionTriggers(tpatrs).GetActionTriggers()
					if err != nil {
						return err
					}

					for _, atrsLst := range atrsM {
						atrs := make([]*ActionTrigger, len(atrsLst))
						for idx, apiAtr := range atrsLst {
							minSleep, _ := utils.ParseDurationWithSecs(apiAtr.MinSleep)
							balanceExpTime, _ := utils.ParseDate(apiAtr.BalanceExpirationDate)
							expTime, _ := utils.ParseTimeDetectLayout(apiAtr.ExpirationDate, tpr.timezone)
							actTime, _ := utils.ParseTimeDetectLayout(apiAtr.ActivationDate, tpr.timezone)
							if apiAtr.UniqueID == "" {
								apiAtr.UniqueID = utils.GenUUID()
							}
							atrs[idx] = &ActionTrigger{
								ID:                    triggerTag,
								UniqueID:              apiAtr.UniqueID,
								ThresholdType:         apiAtr.ThresholdType,
								ThresholdValue:        apiAtr.ThresholdValue,
								Recurrent:             apiAtr.Recurrent,
								MinSleep:              minSleep,
								ExpirationDate:        expTime,
								ActivationDate:        actTime,
								BalanceId:             apiAtr.BalanceId,
								BalanceType:           apiAtr.BalanceType,
								BalanceDirections:     utils.ParseStringMap(apiAtr.BalanceDirections),
								BalanceDestinationIds: utils.ParseStringMap(apiAtr.BalanceDestinationIds),
								BalanceWeight:         apiAtr.BalanceWeight,
								BalanceExpirationDate: balanceExpTime,
								BalanceRatingSubject:  apiAtr.BalanceRatingSubject,
								BalanceCategories:     utils.ParseStringMap(apiAtr.BalanceCategories),
								BalanceSharedGroups:   utils.ParseStringMap(apiAtr.BalanceSharedGroups),
								BalanceTimingTags:     utils.ParseStringMap(apiAtr.BalanceTimingTags),
								Weight:                apiAtr.Weight,
								ActionsId:             apiAtr.ActionsId,
							}
						}
						tpr.actionsTriggers[triggerTag] = atrs
					}
				}
				// collect action ids from triggers
				for _, atr := range tpr.actionsTriggers[triggerTag] {
					actionsIds = append(actionsIds, atr.ActionsId)
				}
			}
			triggers, exists := tpr.actionsTriggers[triggerTag]
			if triggerTag != "" && !exists {
				// only return error if there was something there for the tag
				return fmt.Errorf("could not get action triggers for cdr stats id %s: %s", cs.Id, triggerTag)
			}
			// write action triggers
			err = tpr.ratingStorage.SetActionTriggers(triggerTag, triggers)
			if err != nil {
				return errors.New(err.Error() + " (SetActionTriggers): " + triggerTag)
			}
			UpdateCdrStats(cs, triggers, tpStat, tpr.timezone)
			tpr.cdrStats[tag] = cs
		}
	}
	// actions
	for _, actId := range actionsIds {
		_, exists := tpr.actions[actId]
		if !exists {
			tpas, err := tpr.lr.GetTpActions(tpr.tpid, actId)
			if err != nil {
				return err
			}
			as, err := TpActions(tpas).GetActions()
			if err != nil {
				return err
			}
			for tag, tpacts := range as {
				enacts := make([]*Action, len(tpacts))
				for idx, tpact := range tpacts {
					// check filter field
					if len(tpact.Filter) > 0 {
						if _, err := structmatcher.NewStructMatcher(tpact.Filter); err != nil {
							return fmt.Errorf("error parsing action %s filter field: %v", tag, err)
						}
					}
					enacts[idx] = &Action{
						Id:               tag + strconv.Itoa(idx),
						ActionType:       tpact.Identifier,
						BalanceType:      tpact.BalanceType,
						Weight:           tpact.Weight,
						ExtraParameters:  tpact.ExtraParameters,
						ExpirationString: tpact.ExpiryTime,
						Filter:           tpact.Filter,
						Balance: &Balance{
							Id:             tpact.BalanceId,
							Value:          tpact.Units,
							Weight:         tpact.BalanceWeight,
							RatingSubject:  tpact.RatingSubject,
							Categories:     utils.ParseStringMap(tpact.Categories),
							Directions:     utils.ParseStringMap(tpact.Directions),
							DestinationIds: utils.ParseStringMap(tpact.DestinationIds),
							SharedGroups:   utils.ParseStringMap(tpact.SharedGroups),
							TimingIDs:      utils.ParseStringMap(tpact.TimingTags),
							Blocker:        tpact.BalanceBlocker,
							Disabled:       tpact.BalanceDisabled,
						},
					}
				}
				tpr.actions[tag] = enacts
			}
		}
	}

	if save {
		// write actions
		for k, as := range tpr.actions {
			err = tpr.ratingStorage.SetActions(k, as)
			if err != nil {
				return err
			}
		}
		for _, stat := range tpr.cdrStats {
			if err := tpr.ratingStorage.SetCdrStats(stat); err != nil {
				return err
			}
		}
	}
	return nil
}