// Transparently handles merging between storage data and configuration, useful as local handler
func HandleGetDerivedChargers(acntStorage AccountingStorage, cfg *config.CGRConfig, attrs utils.AttrDerivedChargers) (utils.DerivedChargers, error) {
	var dcs utils.DerivedChargers
	var err error
	strictKey := utils.DerivedChargersKey(attrs.Direction, attrs.Tenant, attrs.Category, attrs.Account, attrs.Subject)
	anySubjKey := utils.DerivedChargersKey(attrs.Direction, attrs.Tenant, attrs.Category, attrs.Account, utils.ANY)
	for _, dcKey := range []string{strictKey, anySubjKey} {
		if dcsDb, err := acntStorage.GetDerivedChargers(dcKey, false); err != nil && err.Error() != utils.ERR_NOT_FOUND {
			return nil, err
		} else if dcsDb != nil {
			dcs = dcsDb
			break
		}
	}
	if dcs == nil {
		dcs = cfg.DerivedChargers
		return dcs, nil
	}
	if cfg.CombinedDerivedChargers {
		for _, cfgDc := range cfg.DerivedChargers {
			if dcs, err = dcs.Append(cfgDc); err != nil {
				return nil, err
			}
		}
	}
	return dcs, nil
}
Пример #2
0
// Used by SM to get all the prepaid CallDescriptors attached to a session
func (rs *Responder) GetSessionRuns(ev *StoredCdr, sRuns *[]*SessionRun) error {
	if rs.Bal != nil {
		return errors.New("Unsupported method on the balancer")
	}
	if ev.Subject == "" {
		ev.Subject = ev.Account
	}
	if upData, err := LoadUserProfile(ev, "ExtraFields"); err != nil {
		return err
	} else {
		udRcv := upData.(*StoredCdr)
		*ev = *udRcv
	}
	attrsDC := &utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), Category: ev.GetCategory(utils.META_DEFAULT), Direction: ev.GetDirection(utils.META_DEFAULT),
		Account: ev.GetAccount(utils.META_DEFAULT), Subject: ev.GetSubject(utils.META_DEFAULT)}
	var dcs utils.DerivedChargers
	if err := rs.GetDerivedChargers(attrsDC, &dcs); err != nil {
		rs.getCache().Cache(utils.GET_SESS_RUNS_CACHE_PREFIX+ev.CgrId, &cache2go.CacheItem{
			Err: err,
		})
		return err
	}
	dcs, _ = dcs.AppendDefaultRun()
	sesRuns := make([]*SessionRun, 0)
	for _, dc := range dcs {
		if !utils.IsSliceMember([]string{utils.META_PREPAID, utils.PREPAID}, ev.GetReqType(dc.ReqTypeField)) {
			continue // We only consider prepaid sessions
		}
		startTime, err := ev.GetAnswerTime(dc.AnswerTimeField, rs.Timezone)
		if err != nil {
			rs.getCache().Cache(utils.GET_SESS_RUNS_CACHE_PREFIX+ev.CgrId, &cache2go.CacheItem{
				Err: err,
			})
			return errors.New("Error parsing answer event start time")
		}
		cd := &CallDescriptor{
			Direction:   ev.GetDirection(dc.DirectionField),
			Tenant:      ev.GetTenant(dc.TenantField),
			Category:    ev.GetCategory(dc.CategoryField),
			Subject:     ev.GetSubject(dc.SubjectField),
			Account:     ev.GetAccount(dc.AccountField),
			Destination: ev.GetDestination(dc.DestinationField),
			TimeStart:   startTime}
		sesRuns = append(sesRuns, &SessionRun{DerivedCharger: dc, CallDescriptor: cd})
	}
	*sRuns = sesRuns
	rs.getCache().Cache(utils.GET_SESS_RUNS_CACHE_PREFIX+ev.CgrId, &cache2go.CacheItem{
		Value: sRuns,
	})
	return nil
}
Пример #3
0
func (sm *FSSessionManager) OnChannelAnswer(ev Event) {
	if ev.MissingParameter() {
		sm.DisconnectSession(ev.GetUUID(), MISSING_PARAMETER, "")
	}
	if _, err := fsock.FS.SendApiCmd(fmt.Sprintf("uuid_setvar %s cgr_reqtype %s\n\n", ev.GetUUID(), ev.GetReqType(""))); err != nil {
		engine.Logger.Err(fmt.Sprintf("Error on attempting to overwrite cgr_type in chan variables: %v", err))
	}
	attrsDC := utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), Category: ev.GetCategory(utils.META_DEFAULT),
		Direction: ev.GetDirection(utils.META_DEFAULT), Account: ev.GetAccount(utils.META_DEFAULT), Subject: ev.GetSubject(utils.META_DEFAULT)}
	var dcs utils.DerivedChargers
	if err := sm.rater.GetDerivedChargers(attrsDC, &dcs); err != nil {
		engine.Logger.Err(fmt.Sprintf("<SessionManager> OnAnswer: could not get derived charging for event %s: %s", ev.GetUUID(), err.Error()))
		sm.DisconnectSession(ev.GetUUID(), SYSTEM_ERROR, "") // Disconnect the session since we are not able to process sessions
		return
	}
	dcs, _ = dcs.AppendDefaultRun()
	s := NewSession(ev, sm, dcs)
	if s != nil {
		sm.sessions = append(sm.sessions, s)
	}
}
Пример #4
0
// Returns MaxSessionTime for an event received in SessionManager, considering DerivedCharging for it
func (rs *Responder) GetDerivedMaxSessionTime(ev *StoredCdr, reply *float64) error {
	if rs.Bal != nil {
		return errors.New("unsupported method on the balancer")
	}
	if ev.Subject == "" {
		ev.Subject = ev.Account
	}
	if upData, err := LoadUserProfile(ev, "ExtraFields"); err != nil {
		return err
	} else {
		udRcv := upData.(*StoredCdr)
		*ev = *udRcv
	}
	maxCallDuration := -1.0
	attrsDC := &utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), Category: ev.GetCategory(utils.META_DEFAULT), Direction: ev.GetDirection(utils.META_DEFAULT),
		Account: ev.GetAccount(utils.META_DEFAULT), Subject: ev.GetSubject(utils.META_DEFAULT)}
	var dcs utils.DerivedChargers
	if err := rs.GetDerivedChargers(attrsDC, &dcs); err != nil {
		return err
	}
	dcs, _ = dcs.AppendDefaultRun()
	for _, dc := range dcs {
		if utils.IsSliceMember([]string{utils.META_RATED, utils.RATED}, ev.GetReqType(dc.ReqTypeField)) { // Only consider prepaid and pseudoprepaid for MaxSessionTime
			continue
		}
		runFilters, _ := utils.ParseRSRFields(dc.RunFilters, utils.INFIELD_SEP)
		matchingAllFilters := true
		for _, dcRunFilter := range runFilters {
			if fltrPass, _ := ev.PassesFieldFilter(dcRunFilter); !fltrPass {
				matchingAllFilters = false
				break
			}
		}
		if !matchingAllFilters { // Do not process the derived charger further if not all filters were matched
			continue
		}
		startTime, err := ev.GetSetupTime(utils.META_DEFAULT, rs.Timezone)
		if err != nil {
			return err
		}
		usage, err := ev.GetDuration(utils.META_DEFAULT)
		if err != nil {
			return err
		}
		if usage == 0 {
			usage = config.CgrConfig().MaxCallDuration
		}
		cd := &CallDescriptor{
			Direction:   ev.GetDirection(dc.DirectionField),
			Tenant:      ev.GetTenant(dc.TenantField),
			Category:    ev.GetCategory(dc.CategoryField),
			Subject:     ev.GetSubject(dc.SubjectField),
			Account:     ev.GetAccount(dc.AccountField),
			Destination: ev.GetDestination(dc.DestinationField),
			TimeStart:   startTime,
			TimeEnd:     startTime.Add(usage),
		}
		var remainingDuration float64
		err = rs.GetMaxSessionTime(cd, &remainingDuration)
		if err != nil {
			*reply = 0
			return err
		}
		if utils.IsSliceMember([]string{utils.META_POSTPAID, utils.POSTPAID}, ev.GetReqType(dc.ReqTypeField)) { // Only consider prepaid and pseudoprepaid for MaxSessionTime
			continue
		}
		// Set maxCallDuration, smallest out of all forked sessions
		if maxCallDuration == -1.0 { // first time we set it /not initialized yet
			maxCallDuration = remainingDuration
		} else if maxCallDuration > remainingDuration {
			maxCallDuration = remainingDuration
		}
	}
	*reply = maxCallDuration
	return nil
}
Пример #5
0
// Process Authorize request from OpenSIPS and communicate back maxdur
func (osm *OsipsSessionManager) OnAuthorize(osipsDagram *osipsdagram.OsipsEvent) {
	ev, _ := NewOsipsEvent(osipsDagram)
	if ev.MissingParameter() {
		cmdNotify := fmt.Sprintf(":cache_store:\nlocal\n%s/cgr_notify\n%s\n2\n\n", ev.GetUUID(), utils.ERR_MANDATORY_IE_MISSING)
		if reply, err := osm.miConn.SendCommand([]byte(cmdNotify)); err != nil || !bytes.HasPrefix(reply, []byte("200 OK")) {
			engine.Logger.Err(fmt.Sprintf("Failed setting cgr_notify variable for accid: %s, err: %v, reply: %s", ev.GetUUID(), err, string(reply)))
		}
		return
	}
	var maxCallDuration time.Duration // This will be the maximum duration this channel will be allowed to last
	var durInitialized bool
	attrsDC := utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), Category: ev.GetCategory(utils.META_DEFAULT), Direction: ev.GetDirection(utils.META_DEFAULT),
		Account: ev.GetAccount(utils.META_DEFAULT), Subject: ev.GetSubject(utils.META_DEFAULT)}
	var dcs utils.DerivedChargers
	if err := osm.rater.GetDerivedChargers(attrsDC, &dcs); err != nil {
		engine.Logger.Err(fmt.Sprintf("<SM-OpenSIPS> OnAuthorize: could not get derived charging for event %s: %s", ev.GetUUID(), err.Error()))
		cmdNotify := fmt.Sprintf(":cache_store:\nlocal\n%s/cgr_notify\n%s\n2\n\n", ev.GetUUID(), utils.ERR_SERVER_ERROR)
		if reply, err := osm.miConn.SendCommand([]byte(cmdNotify)); err != nil || !bytes.HasPrefix(reply, []byte("200 OK")) {
			engine.Logger.Err(fmt.Sprintf("Failed setting cgr_notify variable for accid: %s, err: %v, reply: %s", ev.GetUUID(), err, string(reply)))
		}
		return
	}
	dcs, _ = dcs.AppendDefaultRun()
	for _, dc := range dcs {
		runFilters, _ := utils.ParseRSRFields(dc.RunFilters, utils.INFIELD_SEP)
		matchingAllFilters := true
		for _, dcRunFilter := range runFilters {
			if fltrPass, _ := ev.PassesFieldFilter(dcRunFilter); !fltrPass {
				matchingAllFilters = false
				break
			}
		}
		if !matchingAllFilters { // Do not process the derived charger further if not all filters were matched
			continue
		}
		startTime, err := ev.GetSetupTime(utils.META_DEFAULT)
		if err != nil {
			engine.Logger.Err("Error parsing answer event start time, using time.Now!")
			startTime = time.Now()
		}
		cd := engine.CallDescriptor{
			Direction:   ev.GetDirection(dc.DirectionField),
			Tenant:      ev.GetTenant(dc.TenantField),
			Category:    ev.GetCategory(dc.CategoryField),
			Subject:     ev.GetSubject(dc.SubjectField),
			Account:     ev.GetAccount(dc.AccountField),
			Destination: ev.GetDestination(dc.DestinationField),
			TimeStart:   startTime,
			TimeEnd:     startTime.Add(osm.cgrCfg.SMMaxCallDuration),
		}
		var remainingDurationFloat float64
		err = osm.rater.GetMaxSessionTime(cd, &remainingDurationFloat)
		if err != nil {
			engine.Logger.Err(fmt.Sprintf("Could not get max session time for %s: %v", ev.GetUUID(), err))
			cmdNotify := fmt.Sprintf(":cache_store:\nlocal\n%s/cgr_notify\n%s\n2\n\n", ev.GetUUID(), utils.ERR_SERVER_ERROR)
			if reply, err := osm.miConn.SendCommand([]byte(cmdNotify)); err != nil || !bytes.HasPrefix(reply, []byte("200 OK")) {
				engine.Logger.Err(fmt.Sprintf("Failed setting cgr_notify variable for accid: %s, err: %v, reply: %s", ev.GetUUID(), err, string(reply)))
			}
			return
		}
		remainingDuration := time.Duration(remainingDurationFloat)
		// Set maxCallDuration, smallest out of all forked sessions
		if !durInitialized { // first time we set it /not initialized yet
			maxCallDuration = remainingDuration
			durInitialized = true
		} else if maxCallDuration > remainingDuration {
			maxCallDuration = remainingDuration
		}
	}
	if maxCallDuration <= osm.cgrCfg.SMMinCallDuration {
		cmdNotify := fmt.Sprintf(":cache_store:\nlocal\n%s/cgr_notify\n%s\n2\n\n", ev.GetUUID(), OSIPS_INSUFFICIENT_FUNDS)
		if reply, err := osm.miConn.SendCommand([]byte(cmdNotify)); err != nil || !bytes.HasPrefix(reply, []byte("200 OK")) {
			engine.Logger.Err(fmt.Sprintf("Failed setting cgr_notify variable for accid: %s, err: %v, reply: %s", ev.GetUUID(), err, string(reply)))
		}
		return
	}
	cmdMaxDur := fmt.Sprintf(":cache_store:\nlocal\n%s/cgr_maxdur\n%d\n\n", ev.GetUUID(), int(maxCallDuration.Seconds()))
	if reply, err := osm.miConn.SendCommand([]byte(cmdMaxDur)); err != nil || !bytes.HasPrefix(reply, []byte("200 OK")) {
		engine.Logger.Err(fmt.Sprintf("Failed setting cgr_maxdur variable for accid: %s, err: %v, reply: %s", ev.GetUUID(), err, string(reply)))
	}
	cmdNotify := fmt.Sprintf(":cache_store:\nlocal\n%s/cgr_notify\n%s\n", ev.GetUUID(), OSIPS_AUTH_OK)
	if reply, err := osm.miConn.SendCommand([]byte(cmdNotify)); err != nil || !bytes.HasPrefix(reply, []byte("200 OK")) {
		engine.Logger.Err(fmt.Sprintf("Failed setting cgr_notify variable for accid: %s, err: %v, reply: %s", ev.GetUUID(), err, string(reply)))
	}
}
Пример #6
0
func (sm *FSSessionManager) OnChannelHangupComplete(ev Event) {
	go sm.processCdr(ev.AsStoredCdr())
	s := sm.GetSession(ev.GetUUID())
	if s == nil { // Not handled by us
		return
	} else {
		sm.RemoveSession(s.uuid) // Unreference it early so we avoid concurrency
	}
	defer s.Close(ev) // Stop loop and save the costs deducted so far to database
	attrsDC := utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), Category: ev.GetCategory(utils.META_DEFAULT), Direction: ev.GetDirection(utils.META_DEFAULT),
		Account: ev.GetAccount(utils.META_DEFAULT), Subject: ev.GetSubject(utils.META_DEFAULT)}
	var dcs utils.DerivedChargers
	if err := sm.rater.GetDerivedChargers(attrsDC, &dcs); err != nil {
		engine.Logger.Err(fmt.Sprintf("<SessionManager> OnHangup: could not get derived charging for event %s: %s", ev.GetUUID(), err.Error()))
		return
	}
	dcs, _ = dcs.AppendDefaultRun()
	for _, dc := range dcs {
		if ev.GetReqType(dc.ReqTypeField) != utils.PREPAID {
			continue
		}
		sr := s.GetSessionRun(dc.RunId)
		if sr == nil {
			continue // Did not save a sessionRun for this dc
		}
		if len(sr.callCosts) == 0 {
			continue // why would we have 0 callcosts
		}
		lastCC := sr.callCosts[len(sr.callCosts)-1]
		lastCC.Timespans.Decompress()
		// put credit back
		startTime, err := ev.GetAnswerTime(dc.AnswerTimeField)
		if err != nil {
			engine.Logger.Crit("Error parsing prepaid call start time from event")
			return
		}
		duration, err := ev.GetDuration(dc.UsageField)
		if err != nil {
			engine.Logger.Crit(fmt.Sprintf("Error parsing call duration from event %s", err.Error()))
			return
		}
		hangupTime := startTime.Add(duration)
		end := lastCC.Timespans[len(lastCC.Timespans)-1].TimeEnd
		refundDuration := end.Sub(hangupTime)
		var refundIncrements engine.Increments
		for i := len(lastCC.Timespans) - 1; i >= 0; i-- {
			ts := lastCC.Timespans[i]
			tsDuration := ts.GetDuration()
			if refundDuration <= tsDuration {
				lastRefundedIncrementIndex := 0
				for j := len(ts.Increments) - 1; j >= 0; j-- {
					increment := ts.Increments[j]
					if increment.Duration <= refundDuration {
						refundIncrements = append(refundIncrements, increment)
						refundDuration -= increment.Duration
						lastRefundedIncrementIndex = j
					}
				}
				ts.SplitByIncrement(lastRefundedIncrementIndex)
				break // do not go to other timespans
			} else {
				refundIncrements = append(refundIncrements, ts.Increments...)
				// remove the timespan entirely
				lastCC.Timespans[i] = nil
				lastCC.Timespans = lastCC.Timespans[:i]
				// continue to the next timespan with what is left to refund
				refundDuration -= tsDuration
			}
		}
		// show only what was actualy refunded (stopped in timespan)
		// engine.Logger.Info(fmt.Sprintf("Refund duration: %v", initialRefundDuration-refundDuration))
		if len(refundIncrements) > 0 {
			cd := &engine.CallDescriptor{
				Direction:   lastCC.Direction,
				Tenant:      lastCC.Tenant,
				Category:    lastCC.Category,
				Subject:     lastCC.Subject,
				Account:     lastCC.Account,
				Destination: lastCC.Destination,
				Increments:  refundIncrements,
			}
			var response float64
			err := sm.rater.RefundIncrements(*cd, &response)
			if err != nil {
				engine.Logger.Err(fmt.Sprintf("Debit cents failed: %v", err))
			}
		}
		cost := refundIncrements.GetTotalCost()
		lastCC.Cost -= cost
		lastCC.Timespans.Compress()
	}
}
Пример #7
0
func (sm *FSSessionManager) OnChannelPark(ev Event) {
	var maxCallDuration time.Duration // This will be the maximum duration this channel will be allowed to last
	var durInitialized bool
	attrsDC := utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), Category: ev.GetCategory(utils.META_DEFAULT), Direction: ev.GetDirection(utils.META_DEFAULT),
		Account: ev.GetAccount(utils.META_DEFAULT), Subject: ev.GetSubject(utils.META_DEFAULT)}
	var dcs utils.DerivedChargers
	if err := sm.rater.GetDerivedChargers(attrsDC, &dcs); err != nil {
		engine.Logger.Err(fmt.Sprintf("<SessionManager> OnPark: could not get derived charging for event %s: %s", ev.GetUUID(), err.Error()))
		sm.unparkCall(ev.GetUUID(), ev.GetCallDestNr(utils.META_DEFAULT), SYSTEM_ERROR) // We unpark on original destination
		return
	}
	dcs, _ = dcs.AppendDefaultRun()
	for _, dc := range dcs {
		runFilters, _ := utils.ParseRSRFields(dc.RunFilters, utils.INFIELD_SEP)
		matchingAllFilters := true
		for _, dcRunFilter := range runFilters {
			if fltrPass, _ := ev.PassesFieldFilter(dcRunFilter); !fltrPass {
				matchingAllFilters = false
				break
			}
		}
		if !matchingAllFilters { // Do not process the derived charger further if not all filters were matched
			continue
		}
		startTime, err := ev.GetAnswerTime(PARK_TIME)
		if err != nil {
			engine.Logger.Err("Error parsing answer event start time, using time.Now!")
			startTime = time.Now()
		}
		if ev.MissingParameter() {
			sm.unparkCall(ev.GetUUID(), ev.GetCallDestNr(dc.DestinationField), MISSING_PARAMETER)
			engine.Logger.Err(fmt.Sprintf("Missing parameter for %s", ev.GetUUID()))
			return
		}
		cd := engine.CallDescriptor{
			Direction:   ev.GetDirection(dc.DirectionField),
			Tenant:      ev.GetTenant(dc.TenantField),
			Category:    ev.GetCategory(dc.CategoryField),
			Subject:     ev.GetSubject(dc.SubjectField),
			Account:     ev.GetAccount(dc.AccountField),
			Destination: ev.GetDestination(dc.DestinationField),
			TimeStart:   startTime,
			TimeEnd:     startTime.Add(cfg.SMMaxCallDuration),
		}
		var remainingDurationFloat float64
		err = sm.rater.GetMaxSessionTime(cd, &remainingDurationFloat)
		if err != nil {
			engine.Logger.Err(fmt.Sprintf("Could not get max session time for %s: %v", ev.GetUUID(), err))
			sm.unparkCall(ev.GetUUID(), ev.GetCallDestNr(""), SYSTEM_ERROR) // We unpark on original destination
			return
		}
		remainingDuration := time.Duration(remainingDurationFloat)
		// Set maxCallDuration, smallest out of all forked sessions
		if !durInitialized { // first time we set it /not initialized yet
			maxCallDuration = remainingDuration
			durInitialized = true
		} else if maxCallDuration > remainingDuration {
			maxCallDuration = remainingDuration
		}
	}
	if maxCallDuration <= cfg.SMMinCallDuration {
		//engine.Logger.Info(fmt.Sprintf("Not enough credit for trasferring the call %s for %s.", ev.GetUUID(), cd.GetKey(cd.Subject)))
		sm.unparkCall(ev.GetUUID(), ev.GetCallDestNr(utils.META_DEFAULT), INSUFFICIENT_FUNDS)
		return
	}
	sm.setMaxCallDuration(ev.GetUUID(), maxCallDuration)
	sm.unparkCall(ev.GetUUID(), ev.GetCallDestNr(utils.META_DEFAULT), AUTH_OK)
}