Fix merge conflicts, improving creation of new rpc pool

This commit is contained in:
DanB
2016-03-24 09:18:38 +01:00
155 changed files with 8685 additions and 3439 deletions

View File

@@ -100,7 +100,7 @@ func (sm *FSSessionManager) setCgrLcr(ev engine.Event, connId string) error {
return err
}
cd := &engine.CallDescriptor{
CgrId: ev.GetCgrId(sm.Timezone()),
CgrID: ev.GetCgrId(sm.Timezone()),
Direction: ev.GetDirection(utils.META_DEFAULT),
Tenant: ev.GetTenant(utils.META_DEFAULT),
Category: ev.GetCategory(utils.META_DEFAULT),
@@ -149,7 +149,7 @@ func (sm *FSSessionManager) onChannelPark(ev engine.Event, connId string) {
// ComputeLcr
if ev.ComputeLcr() {
cd, err := fsev.AsCallDescriptor()
cd.CgrId = fsev.GetCgrId(sm.Timezone())
cd.CgrID = fsev.GetCgrId(sm.Timezone())
if err != nil {
utils.Logger.Info(fmt.Sprintf("<SM-FreeSWITCH> LCR_PREPROCESS_ERROR: %s", err.Error()))
sm.unparkCall(ev.GetUUID(), connId, ev.GetCallDestNr(utils.META_DEFAULT), SYSTEM_ERROR)

View File

@@ -103,7 +103,7 @@ func (self *KamailioSessionManager) onCgrLcrReq(evData []byte, connId string) {
func (self *KamailioSessionManager) getSuppliers(kev KamEvent) (string, error) {
cd, err := kev.AsCallDescriptor()
cd.CgrId = kev.GetCgrId(self.timezone)
cd.CgrID = kev.GetCgrId(self.timezone)
if err != nil {
utils.Logger.Info(fmt.Sprintf("<SM-Kamailio> LCR_PREPROCESS_ERROR error: %s", err.Error()))
return "", errors.New("LCR_PREPROCESS_ERROR")

View File

@@ -70,7 +70,7 @@ func NewSession(ev engine.Event, connId string, sm SessionManager) *Session {
// the debit loop method (to be stoped by sending somenthing on stopDebit channel)
func (s *Session) debitLoop(runIdx int) {
nextCd := s.sessionRuns[runIdx].CallDescriptor
nextCd.CgrId = s.eventStart.GetCgrId(s.sessionManager.Timezone())
nextCd.CgrID = s.eventStart.GetCgrId(s.sessionManager.Timezone())
index := 0.0
debitPeriod := s.sessionManager.DebitInterval()
for {
@@ -180,6 +180,7 @@ func (s *Session) Refund(lastCC *engine.CallCost, hangupTime time.Time) error {
lastCC.Timespans = lastCC.Timespans[:i]
} else {
ts.SplitByIncrement(lastRefundedIncrementIndex)
ts.Cost = ts.CalculateCost()
}
break // do not go to other timespans
} else {
@@ -195,7 +196,7 @@ func (s *Session) Refund(lastCC *engine.CallCost, hangupTime time.Time) error {
// utils.Logger.Info(fmt.Sprintf("Refund duration: %v", initialRefundDuration-refundDuration))
if len(refundIncrements) > 0 {
cd := &engine.CallDescriptor{
CgrId: s.eventStart.GetCgrId(s.sessionManager.Timezone()),
CgrID: s.eventStart.GetCgrId(s.sessionManager.Timezone()),
Direction: lastCC.Direction,
Tenant: lastCC.Tenant,
Category: lastCC.Category,
@@ -215,6 +216,7 @@ func (s *Session) Refund(lastCC *engine.CallCost, hangupTime time.Time) error {
}
//utils.Logger.Debug(fmt.Sprintf("REFUND INCR: %s", utils.ToJSON(refundIncrements)))
lastCC.Cost -= refundIncrements.GetTotalCost()
lastCC.UpdateRatedUsage()
lastCC.Timespans.Compress()
return nil
}
@@ -237,6 +239,16 @@ func (s *Session) SaveOperations() {
}
firstCC.Timespans.Compress()
firstCC.Round()
roundIncrements := firstCC.GetRoundIncrements()
if len(roundIncrements) != 0 {
cd := firstCC.CreateCallDescriptor()
cd.Increments = roundIncrements
var response float64
if err := s.sessionManager.Rater().Call("Responder.RefundRounding", cd, &response); err != nil {
utils.Logger.Err(fmt.Sprintf("<SM> ERROR failed to refund rounding: %v", err))
}
}
var reply string
err := s.sessionManager.CdrSrv().Call("CdrServer.LogCallCost", &engine.CallCostLog{
CgrId: s.eventStart.GetCgrId(s.sessionManager.Timezone()),

View File

@@ -89,6 +89,20 @@ func (mc *MockRpcClient) Call(methodName string, arg interface{}, reply interfac
}
return nil
}
func (mc *MockConnector) GetCost(*engine.CallDescriptor, *engine.CallCost) error { return nil }
func (mc *MockConnector) Debit(*engine.CallDescriptor, *engine.CallCost) error { return nil }
func (mc *MockConnector) MaxDebit(*engine.CallDescriptor, *engine.CallCost) error { return nil }
func (mc *MockConnector) RefundIncrements(cd *engine.CallDescriptor, reply *float64) error {
mc.refundCd = cd
return nil
}
func (mc *MockConnector) RefundRounding(cd *engine.CallDescriptor, reply *float64) error {
return nil
}
func (mc *MockConnector) GetMaxSessionTime(*engine.CallDescriptor, *float64) error { return nil }
func (mc *MockConnector) GetDerivedChargers(*utils.AttrDerivedChargers, *utils.DerivedChargers) error {
return nil
}
func TestSessionRefund(t *testing.T) {
mc := &MockRpcClient{}

View File

@@ -20,6 +20,7 @@ package sessionmanager
import (
"encoding/json"
"fmt"
"strconv"
"time"
@@ -49,8 +50,9 @@ func (self SMGenericEvent) GetTOR(fieldName string) string {
}
func (self SMGenericEvent) GetCgrId(timezone string) string {
setupTime, _ := self.GetSetupTime(utils.META_DEFAULT, timezone)
return utils.Sha1(self.GetUUID(), setupTime.UTC().String())
//setupTime, _ := self.GetSetupTime(utils.META_DEFAULT, timezone)
//return utils.Sha1(self.GetUUID(), setupTime.UTC().String())
return utils.Sha1(self.GetUUID())
}
func (self SMGenericEvent) GetUUID() string {
@@ -155,7 +157,23 @@ func (self SMGenericEvent) GetUsage(fieldName string) (time.Duration, error) {
if fieldName == utils.META_DEFAULT {
fieldName = utils.USAGE
}
result, _ := utils.ConvertIfaceToString(self[fieldName])
valIf, hasVal := self[fieldName]
if !hasVal {
return nilDuration, utils.ErrNotFound
}
result, _ := utils.ConvertIfaceToString(valIf)
return utils.ParseDurationWithSecs(result)
}
func (self SMGenericEvent) GetLastUsed(fieldName string) (time.Duration, error) {
if fieldName == utils.META_DEFAULT {
fieldName = utils.LastUsed
}
valStr, hasVal := self[fieldName]
if !hasVal {
return nilDuration, utils.ErrNotFound
}
result, _ := utils.ConvertIfaceToString(valStr)
return utils.ParseDurationWithSecs(result)
}
@@ -210,7 +228,7 @@ func (self SMGenericEvent) GetCdrSource() string {
func (self SMGenericEvent) GetExtraFields() map[string]string {
extraFields := make(map[string]string)
for key, val := range self {
primaryFields := append(utils.PrimaryCdrFields, utils.EVENT_NAME)
primaryFields := append(utils.PrimaryCdrFields, utils.EVENT_NAME, utils.LastUsed)
if utils.IsSliceMember(primaryFields, key) {
continue
}
@@ -348,3 +366,16 @@ func (self SMGenericEvent) AsLcrRequest() *engine.LcrRequest {
Duration: usageStr,
}
}
// AsMapStringString Converts into map[string]string, used for example as pubsub event
func (self SMGenericEvent) AsMapStringString() (map[string]string, error) {
mp := make(map[string]string)
for k, v := range self {
if strV, casts := utils.CastIfToString(v); !casts {
return nil, fmt.Errorf("Value %+v does not cast to string", v)
} else {
mp[k] = strV
}
}
return mp, nil
}

View File

@@ -45,6 +45,7 @@ func TestSMGenericEventParseFields(t *testing.T) {
smGev[utils.SETUP_TIME] = "2015-11-09 14:21:24"
smGev[utils.ANSWER_TIME] = "2015-11-09 14:22:02"
smGev[utils.USAGE] = "1m23s"
smGev[utils.LastUsed] = "21s"
smGev[utils.PDD] = "300ms"
smGev[utils.SUPPLIER] = "supplier1"
smGev[utils.DISCONNECT_CAUSE] = "NORMAL_DISCONNECT"
@@ -54,7 +55,7 @@ func TestSMGenericEventParseFields(t *testing.T) {
if smGev.GetName() != "TEST_EVENT" {
t.Error("Unexpected: ", smGev.GetName())
}
if smGev.GetCgrId("UTC") != "0711eaa78e53937f1593dabc08c83ea04a915f2e" {
if smGev.GetCgrId("UTC") != "8cb2237d0679ca88db6464eac60da96345513964" {
t.Error("Unexpected: ", smGev.GetCgrId("UTC"))
}
if smGev.GetUUID() != "12345" {
@@ -107,6 +108,11 @@ func TestSMGenericEventParseFields(t *testing.T) {
} else if dur != time.Duration(83)*time.Second {
t.Error("Unexpected: ", dur)
}
if lastUsed, err := smGev.GetLastUsed(utils.META_DEFAULT); err != nil {
t.Error(err)
} else if lastUsed != time.Duration(21)*time.Second {
t.Error("Unexpected: ", lastUsed)
}
if pdd, err := smGev.GetPdd(utils.META_DEFAULT); err != nil {
t.Error(err)
} else if pdd != time.Duration(300)*time.Millisecond {
@@ -147,7 +153,7 @@ func TestSMGenericEventAsStoredCdr(t *testing.T) {
smGev[utils.CDRHOST] = "10.0.3.15"
smGev["Extra1"] = "Value1"
smGev["Extra2"] = 5
eStoredCdr := &engine.CDR{CGRID: "0711eaa78e53937f1593dabc08c83ea04a915f2e",
eStoredCdr := &engine.CDR{CGRID: "8cb2237d0679ca88db6464eac60da96345513964",
ToR: utils.SMS, OriginID: "12345", OriginHost: "10.0.3.15", Source: "SMG_TEST_EVENT", RequestType: utils.META_PREPAID,
Direction: utils.OUT, Tenant: "cgrates.org", Category: "call", Account: "account1", Subject: "subject1",
Destination: "+4986517174963", SetupTime: time.Date(2015, 11, 9, 14, 21, 24, 0, time.UTC), AnswerTime: time.Date(2015, 11, 9, 14, 22, 2, 0, time.UTC),

View File

@@ -138,7 +138,7 @@ func TestSMGMonetaryRefund(t *testing.T) {
}
var acnt *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
eAcntVal := 8.699800
eAcntVal := 8.700010
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal {
@@ -268,7 +268,7 @@ func TestSMGMixedRefund(t *testing.T) {
//var acnt *engine.Account
//attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
eVoiceVal := 90.0
eMoneyVal := 8.739
eMoneyVal := 8.7399
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.VOICE].GetTotalValue() != eVoiceVal ||
@@ -305,3 +305,120 @@ func TestSMGMixedRefund(t *testing.T) {
t.Logf("After monetary: %f", acnt.BalanceMap[utils.MONETARY].GetTotalValue())
t.Logf("After voice: %f", acnt.BalanceMap[utils.VOICE].GetTotalValue())
}
func TestSMGLastUsed(t *testing.T) {
if !*testIntegration {
return
}
var acnt *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
eAcntVal := 8.790000
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue())
}
smgEv := SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.VOICE,
utils.ACCID: "12349",
utils.DIRECTION: utils.OUT,
utils.ACCOUNT: "1001",
utils.SUBJECT: "1001",
utils.DESTINATION: "1006",
utils.CATEGORY: "call",
utils.TENANT: "cgrates.org",
utils.REQTYPE: utils.META_PREPAID,
utils.SETUP_TIME: "2016-01-05 18:30:49",
utils.ANSWER_TIME: "2016-01-05 18:31:05",
utils.USAGE: "2m",
}
var maxUsage float64
if err := smgRPC.Call("SMGenericV1.SessionStart", smgEv, &maxUsage); err != nil {
t.Error(err)
}
if maxUsage != 120 {
t.Error("Bad max usage: ", maxUsage)
}
eAcntVal = 7.39002
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue())
}
smgEv = SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.VOICE,
utils.ACCID: "12349",
utils.DIRECTION: utils.OUT,
utils.ACCOUNT: "1001",
utils.SUBJECT: "1001",
utils.DESTINATION: "1006",
utils.CATEGORY: "call",
utils.TENANT: "cgrates.org",
utils.REQTYPE: utils.META_PREPAID,
utils.USAGE: "2m",
utils.LastUsed: "1m30s",
}
if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil {
t.Error(err)
}
if maxUsage != 120 {
t.Error("Bad max usage: ", maxUsage)
}
eAcntVal = 7.09005
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue())
}
smgEv = SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.VOICE,
utils.ACCID: "12349",
utils.DIRECTION: utils.OUT,
utils.ACCOUNT: "1001",
utils.SUBJECT: "1001",
utils.DESTINATION: "1006",
utils.CATEGORY: "call",
utils.TENANT: "cgrates.org",
utils.REQTYPE: utils.META_PREPAID,
utils.USAGE: "2m",
utils.LastUsed: "2m30s",
}
if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil {
t.Error(err)
}
if maxUsage != 120 {
t.Error("Bad max usage: ", maxUsage)
}
eAcntVal = 6.5901
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue())
}
smgEv = SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.VOICE,
utils.ACCID: "12349",
utils.DIRECTION: utils.OUT,
utils.ACCOUNT: "1001",
utils.SUBJECT: "1001",
utils.DESTINATION: "1006",
utils.CATEGORY: "call",
utils.TENANT: "cgrates.org",
utils.REQTYPE: utils.META_PREPAID,
utils.USAGE: "1m",
}
var rpl string
if err = smgRPC.Call("SMGenericV1.SessionEnd", smgEv, &rpl); err != nil || rpl != utils.OK {
t.Error(err)
}
eAcntVal = 7.59
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue())
}
}

View File

@@ -42,6 +42,8 @@ type SMGSession struct {
sessionCds []*engine.CallDescriptor
callCosts []*engine.CallCost
extraDuration time.Duration // keeps the current duration debited on top of what heas been asked
lastUsage time.Duration // Keep record of the last debit for LastUsed functionality
totalUsage time.Duration
}
// Called in case of automatic debits
@@ -53,7 +55,7 @@ func (self *SMGSession) debitLoop(debitInterval time.Duration) {
return
default:
}
if maxDebit, err := self.debit(debitInterval); err != nil {
if maxDebit, err := self.debit(debitInterval, nilDuration); err != nil {
utils.Logger.Err(fmt.Sprintf("<SMGeneric> Could not complete debit opperation on session: %s, error: %s", self.eventStart.GetUUID(), err.Error()))
disconnectReason := SYSTEM_ERROR
if err.Error() == utils.ErrUnauthorizedDestination.Error() {
@@ -76,11 +78,21 @@ func (self *SMGSession) debitLoop(debitInterval time.Duration) {
}
// Attempts to debit a duration, returns maximum duration which can be debitted or error
func (self *SMGSession) debit(dur time.Duration) (time.Duration, error) {
func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.Duration, error) {
lastUsedCorrection := time.Duration(0) // Used if lastUsed influences the debit
if self.cd.DurationIndex != 0 && lastUsed != 0 {
if self.lastUsage > lastUsed { // We have debitted more than we have used, refund in the duration debitted
lastUsedCorrection = -(self.lastUsage - lastUsed)
} else { // We have debitted less than we have consumed, add the difference to duration debitted
lastUsedCorrection = lastUsed - self.lastUsage
}
}
// apply the lastUsed correction
dur += lastUsedCorrection
self.totalUsage += dur // Should reflect the total usage so far
// apply correction from previous run
dur -= self.extraDuration
self.extraDuration = 0
if self.cd.LoopIndex > 0 {
self.cd.TimeStart = self.cd.TimeEnd
}
@@ -88,6 +100,7 @@ func (self *SMGSession) debit(dur time.Duration) (time.Duration, error) {
self.cd.DurationIndex += dur
cc := &engine.CallCost{}
if err := self.rater.Call("Responder.MaxDebit", self.cd, cc); err != nil {
self.lastUsage = 0
return 0, err
}
// cd corrections
@@ -103,17 +116,22 @@ func (self *SMGSession) debit(dur time.Duration) (time.Duration, error) {
self.cd.LoopIndex += 1
self.sessionCds = append(self.sessionCds, self.cd.Clone())
self.callCosts = append(self.callCosts, cc)
ccDuration -= lastUsedCorrection
if ccDuration < 0 { // if correction has pushed ccDuration bellow 0
ccDuration = 0
}
self.lastUsage = ccDuration // Reset the lastUsage for later reference
return ccDuration, nil
}
// Attempts to refund a duration, error on failure
func (self *SMGSession) refund(refundDuration time.Duration) error {
initialRefundDuration := refundDuration
lastCC := self.callCosts[len(self.callCosts)-1]
lastCC.Timespans.Decompress()
firstCC := self.callCosts[0] // use merged cc (from close function)
firstCC.Timespans.Decompress()
var refundIncrements engine.Increments
for i := len(lastCC.Timespans) - 1; i >= 0; i-- {
ts := lastCC.Timespans[i]
for i := len(firstCC.Timespans) - 1; i >= 0; i-- {
ts := firstCC.Timespans[i]
tsDuration := ts.GetDuration()
if refundDuration <= tsDuration {
@@ -129,17 +147,18 @@ func (self *SMGSession) refund(refundDuration time.Duration) error {
}
}
if lastRefundedIncrementIndex == 0 {
lastCC.Timespans[i] = nil
lastCC.Timespans = lastCC.Timespans[:i]
firstCC.Timespans[i] = nil
firstCC.Timespans = firstCC.Timespans[:i]
} else {
ts.SplitByIncrement(lastRefundedIncrementIndex)
ts.Cost = ts.CalculateCost()
}
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]
firstCC.Timespans[i] = nil
firstCC.Timespans = firstCC.Timespans[:i]
// continue to the next timespan with what is left to refund
refundDuration -= tsDuration
}
@@ -147,16 +166,8 @@ func (self *SMGSession) refund(refundDuration time.Duration) error {
// show only what was actualy refunded (stopped in timespan)
// utils.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,
TOR: lastCC.TOR,
Increments: refundIncrements,
}
cd := firstCC.CreateCallDescriptor()
cd.Increments = refundIncrements
cd.Increments.Compress()
utils.Logger.Info(fmt.Sprintf("Refunding duration %v with cd: %s", initialRefundDuration, utils.ToJSON(cd)))
var response float64
@@ -165,17 +176,20 @@ func (self *SMGSession) refund(refundDuration time.Duration) error {
return err
}
}
//utils.Logger.Debug(fmt.Sprintf("REFUND INCR: %s", utils.ToJSON(refundIncrements)))
lastCC.Cost -= refundIncrements.GetTotalCost()
lastCC.Timespans.Compress()
firstCC.Cost -= refundIncrements.GetTotalCost()
firstCC.UpdateRatedUsage()
firstCC.Timespans.Compress()
return nil
}
// Session has ended, check debits and refund the extra charged duration
func (self *SMGSession) close(endTime time.Time) error {
firstCC := self.callCosts[0]
for _, cc := range self.callCosts[1:] {
firstCC.Merge(cc)
}
if len(self.callCosts) != 0 { // We have had at least one cost calculation
lastCC := self.callCosts[len(self.callCosts)-1]
end := lastCC.GetEndTime()
end := firstCC.GetEndTime()
refundDuration := end.Sub(endTime)
self.refund(refundDuration)
}
@@ -206,11 +220,19 @@ func (self *SMGSession) saveOperations() error {
if len(self.callCosts) == 0 {
return nil // There are no costs to save, ignore the operation
}
firstCC := self.callCosts[0]
for _, cc := range self.callCosts[1:] {
firstCC.Merge(cc)
}
firstCC := self.callCosts[0] // was merged in close methos
firstCC.Timespans.Compress()
firstCC.Round()
roundIncrements := firstCC.GetRoundIncrements()
if len(roundIncrements) != 0 {
cd := firstCC.CreateCallDescriptor()
cd.Increments = roundIncrements
var response float64
if err := self.rater.Call("Responder.RefundRounding", cd, &response); err != nil {
return err
}
}
var reply string
err := self.cdrsrv.Call("CdrServer.LogCallCost", &engine.CallCostLog{
CgrId: self.eventStart.GetCgrId(self.timezone),
@@ -231,3 +253,44 @@ func (self *SMGSession) saveOperations() error {
}
return nil
}
func (self *SMGSession) TotalUsage() time.Duration {
return self.totalUsage
}
func (self *SMGSession) AsActiveSession(timezone string) *ActiveSession {
sTime, _ := self.eventStart.GetSetupTime(utils.META_DEFAULT, timezone)
aTime, _ := self.eventStart.GetAnswerTime(utils.META_DEFAULT, timezone)
usage, _ := self.eventStart.GetUsage(utils.META_DEFAULT)
pdd, _ := self.eventStart.GetPdd(utils.META_DEFAULT)
aSession := &ActiveSession{
CgrId: self.eventStart.GetCgrId(timezone),
TOR: utils.VOICE,
RunId: self.runId,
AccId: self.eventStart.GetUUID(),
CdrHost: self.eventStart.GetOriginatorIP(utils.META_DEFAULT),
CdrSource: self.eventStart.GetCdrSource(),
ReqType: self.eventStart.GetReqType(utils.META_DEFAULT),
Direction: self.eventStart.GetDirection(utils.META_DEFAULT),
Tenant: self.eventStart.GetTenant(utils.META_DEFAULT),
Category: self.eventStart.GetCategory(utils.META_DEFAULT),
Account: self.eventStart.GetAccount(utils.META_DEFAULT),
Subject: self.eventStart.GetSubject(utils.META_DEFAULT),
Destination: self.eventStart.GetDestination(utils.META_DEFAULT),
SetupTime: sTime,
AnswerTime: aTime,
Usage: usage,
Pdd: pdd,
ExtraFields: self.eventStart.GetExtraFields(),
Supplier: self.eventStart.GetSupplier(utils.META_DEFAULT),
SMId: "CGR-DA",
}
if self.cd != nil {
aSession.LoopIndex = self.cd.LoopIndex
aSession.DurationIndex = self.cd.DurationIndex
aSession.MaxRate = self.cd.MaxRate
aSession.MaxRateUnit = self.cd.MaxRateUnit
aSession.MaxCostSoFar = self.cd.MaxCostSoFar
}
return aSession
}

View File

@@ -97,6 +97,7 @@ func (self *SMGeneric) sessionStart(evStart SMGenericEvent, connId string) error
s := &SMGSession{eventStart: evStart, connId: connId, runId: sessionRun.DerivedCharger.RunID, timezone: self.timezone,
rater: self.rater, cdrsrv: self.cdrsrv, cd: sessionRun.CallDescriptor}
self.indexSession(sessionId, s)
//utils.Logger.Info(fmt.Sprintf("<SMGeneric> Starting session: %s, runId: %s", sessionId, s.runId))
if self.cgrCfg.SmGenericConfig.DebitInterval != 0 {
s.stopDebit = stopDebitChan
go s.debitLoop(self.cgrCfg.SmGenericConfig.DebitInterval)
@@ -118,6 +119,7 @@ func (self *SMGeneric) sessionEnd(sessionId string, usage time.Duration) error {
return nil, nil // Did not find the session so no need to close it anymore
}
for idx, s := range ss {
//utils.Logger.Info(fmt.Sprintf("<SMGeneric> Ending session: %s, runId: %s", sessionId, s.runId))
if idx == 0 && s.stopDebit != nil {
close(s.stopDebit) // Stop automatic debits
}
@@ -153,7 +155,7 @@ func (self *SMGeneric) GetMaxUsage(gev SMGenericEvent, clnt *rpc2.Client) (time.
func (self *SMGeneric) GetLcrSuppliers(gev SMGenericEvent, clnt *rpc2.Client) ([]string, error) {
gev[utils.EVENT_NAME] = utils.CGR_LCR_REQUEST
cd, err := gev.AsLcrRequest().AsCallDescriptor(self.timezone)
cd.CgrId = gev.GetCgrId(self.timezone)
cd.CgrID = gev.GetCgrId(self.timezone)
if err != nil {
return nil, err
}
@@ -170,18 +172,23 @@ func (self *SMGeneric) GetLcrSuppliers(gev SMGenericEvent, clnt *rpc2.Client) ([
// Execute debits for usage/maxUsage
func (self *SMGeneric) SessionUpdate(gev SMGenericEvent, clnt *rpc2.Client) (time.Duration, error) {
evLastUsed, err := gev.GetLastUsed(utils.META_DEFAULT)
if err != nil && err != utils.ErrNotFound {
return nilDuration, err
}
evMaxUsage, err := gev.GetMaxUsage(utils.META_DEFAULT, self.cgrCfg.MaxCallDuration)
if err != nil {
if err == utils.ErrNotFound {
err = utils.ErrMandatoryIeMissing
}
return nilDuration, err
}
evUuid := gev.GetUUID()
for _, s := range self.getSession(evUuid) {
if maxDur, err := s.debit(evMaxUsage); err != nil {
if maxDur, err := s.debit(evMaxUsage, evLastUsed); err != nil {
return nilDuration, err
} else {
if maxDur < evMaxUsage {
evMaxUsage = maxDur
}
} else if maxDur < evMaxUsage {
evMaxUsage = maxDur
}
}
return evMaxUsage, nil
@@ -199,7 +206,25 @@ func (self *SMGeneric) SessionStart(gev SMGenericEvent, clnt *rpc2.Client) (time
func (self *SMGeneric) SessionEnd(gev SMGenericEvent, clnt *rpc2.Client) error {
usage, err := gev.GetUsage(utils.META_DEFAULT)
if err != nil {
return err
if err != utils.ErrNotFound {
return err
}
lastUsed, err := gev.GetLastUsed(utils.META_DEFAULT)
if err != nil {
if err == utils.ErrNotFound {
err = utils.ErrMandatoryIeMissing
}
return err
}
var s *SMGSession
for _, s = range self.getSession(gev.GetUUID()) {
break
}
if s == nil {
return nil
}
usage = s.TotalUsage() + lastUsed
}
if err := self.sessionEnd(gev.GetUUID(), usage); err != nil {
return err
@@ -215,18 +240,18 @@ func (self *SMGeneric) ChargeEvent(gev SMGenericEvent, clnt *rpc2.Client) (maxDu
} else if len(sessionRuns) == 0 {
return nilDuration, nil
}
var maxDurInit bool // Avoid differences between default 0 and received 0
for _, sR := range sessionRuns {
cc := new(engine.CallCost)
if err := self.rater.Call("Responder.MaxDebit", sR.CallDescriptor, cc); err != nil {
withErrors = true
if err = self.rater.Call("Responder.MaxDebit", sR.CallDescriptor, cc); err != nil {
utils.Logger.Err(fmt.Sprintf("<SMGeneric> Could not Debit CD: %+v, RunID: %s, error: %s", sR.CallDescriptor, sR.DerivedCharger.RunID, err.Error()))
break
}
sR.CallCosts = append(sR.CallCosts, cc) // Save it so we can revert on issues
if ccDur := cc.GetDuration(); ccDur == 0 {
err = errors.New("INSUFFICIENT_FUNDS")
err = utils.ErrInsufficientCredit
break
} else if ccDur < maxDur {
} else if !maxDurInit || ccDur < maxDur {
maxDur = ccDur
}
}
@@ -262,7 +287,7 @@ func (self *SMGeneric) ChargeEvent(gev SMGenericEvent, clnt *rpc2.Client) (maxDu
cd.Increments.Compress()
utils.Logger.Info(fmt.Sprintf("Refunding session run callcost: %s", utils.ToJSON(cd)))
var response float64
err := self.rater.RefundIncrements(cd, &response)
err := self.rater.Call("Responder.RefundIncrements", cd, &response)
if err != nil {
return nilDuration, err
}
@@ -281,8 +306,19 @@ func (self *SMGeneric) ChargeEvent(gev SMGenericEvent, clnt *rpc2.Client) (maxDu
cc.Merge(ccSR)
}
}
cc.Round()
roundIncrements := cc.GetRoundIncrements()
if len(roundIncrements) != 0 {
cd := cc.CreateCallDescriptor()
cd.Increments = roundIncrements
var response float64
if err := self.rater.Call("Responder.RefundRounding", cd, &response); err != nil {
utils.Logger.Err(fmt.Sprintf("<SM> ERROR failed to refund rounding: %v", err))
}
}
var reply string
if err := self.cdrsrv.Call("CdrServer.LogCallCost", &engine.CallCostLog{
if err := self.cdrsrv.Call("CdrsV2.LogCallCost", &engine.CallCostLog{
CgrId: gev.GetCgrId(self.timezone),
Source: utils.SESSION_MANAGER_SOURCE,
RunId: sR.DerivedCharger.RunID,
@@ -311,6 +347,15 @@ func (self *SMGeneric) Connect() error {
return nil
}
// Used by APIer to retrieve sessions
func (self *SMGeneric) Sessions() map[string][]*SMGSession {
return self.getSessions()
}
func (self *SMGeneric) Timezone() string {
return self.timezone
}
// System shutdown
func (self *SMGeneric) Shutdown() error {
for ssId := range self.getSessions() { // Force sessions shutdown