FreeSWITCHAgent with operational *prepaid calls, SessionS with support of Sessions with RunID: *none

This commit is contained in:
DanB
2018-01-12 18:31:05 +01:00
parent 9b1280eb74
commit 63a83e655b
10 changed files with 60 additions and 672 deletions

View File

@@ -222,16 +222,6 @@ func (sm *FSSessionManager) onChannelAnswer(fsev FSEvent, connId string) {
sm.disconnectSession(connId, chanUUID, "", utils.ErrServerError.Error())
return
}
if initSessionArgs.InitSession && *initReply.MaxUsage == time.Duration(-1*time.Second) {
if _, err := sm.conns[connId].SendApiCmd(
fmt.Sprintf("uuid_setvar %s %s false\n\n", fsev.GetUUID(), CGRSessionS)); err != nil {
utils.Logger.Err(
fmt.Sprintf("<%s> error %s setting channel variabile: %s",
utils.FreeSWITCHAgent, err.Error(), CGRSessionS))
sm.disconnectSession(connId, chanUUID, "", utils.ErrServerError.Error())
return
}
}
if initSessionArgs.AllocateResources {
if initReply.ResourceAllocation == nil {
sm.disconnectSession(connId, chanUUID, "",
@@ -245,12 +235,14 @@ func (sm *FSSessionManager) onChannelHangupComplete(fsev FSEvent, connId string)
return
}
var reply string
if err := sm.smg.Call(utils.SessionSv1TerminateSession,
fsev.V1TerminateSessionArgs(), &reply); err != nil {
utils.Logger.Err(
fmt.Sprintf("<SM-FreeSWITCH> Could not terminate session with event %s, error: %s",
fsev.GetUUID(), err.Error()))
return
if fsev[VarAnswerEpoch] != "0" { // call was answered
if err := sm.smg.Call(utils.SessionSv1TerminateSession,
fsev.V1TerminateSessionArgs(), &reply); err != nil {
utils.Logger.Err(
fmt.Sprintf("<SM-FreeSWITCH> Could not terminate session with event %s, error: %s",
fsev.GetUUID(), err.Error()))
return
}
}
if sm.cfg.CreateCdr {
cdr := fsev.AsCDR(sm.timezone)

View File

@@ -33,9 +33,6 @@ import (
// ToDo: Introduce support for RSRFields
// Event type holding a mapping of all event's proprieties
type FSEvent map[string]string
const (
// Freswitch event proprities names
DIRECTION = "Call-Direction"
@@ -75,13 +72,19 @@ const (
SubSResourceS = "resources"
SubSAttributeS = "attributes"
CGRResourceAllocation = "cgr_resource_allocation"
CGRSessionS = "cgr_sessions"
VarCGRSessionS = "variable_" + CGRSessionS
VAR_CGR_DISCONNECT_CAUSE = "variable_" + utils.CGR_DISCONNECT_CAUSE
VAR_CGR_CMPUTELCR = "variable_" + utils.CGR_COMPUTELCR
FsConnID = "FsConnID" // used to share connID info in event
VarAnswerEpoch = "variable_answer_epoch"
)
func NewFSEvent(strEv string) (fsev FSEvent) {
return fsock.FSEventStrToMap(strEv, nil)
}
// Event type holding a mapping of all event's proprieties
type FSEvent map[string]string
// Nice printing for the event object.
func (fsev FSEvent) String() (result string) {
for k, v := range fsev {
@@ -91,10 +94,6 @@ func (fsev FSEvent) String() (result string) {
return
}
func NewFSEvent(strEv string) (fsev FSEvent) {
return fsock.FSEventStrToMap(strEv, nil)
}
func (fsev FSEvent) GetName() string {
return fsev[NAME]
}
@@ -143,10 +142,6 @@ func (fsev FSEvent) GetCategory(fieldName string) string {
}
return utils.FirstNonEmpty(fsev[fieldName], fsev[CATEGORY], config.CgrConfig().DefaultCategory)
}
func (fsev FSEvent) GetCgrId(timezone string) string {
setupTime, _ := fsev.GetSetupTime(utils.META_DEFAULT, timezone)
return utils.Sha1(fsev[UUID], setupTime.UTC().String())
}
func (fsev FSEvent) GetUUID() string {
return fsev[UUID]
}
@@ -282,8 +277,6 @@ func (fsev FSEvent) GetExtraFields() map[string]string {
// Used in derived charging and sittuations when we need to run regexp on fields
func (fsev FSEvent) ParseEventValue(rsrFld *utils.RSRField, timezone string) string {
switch rsrFld.Id {
case utils.CGRID:
return rsrFld.ParseValue(fsev.GetCgrId(timezone))
case utils.TOR:
return rsrFld.ParseValue(utils.VOICE)
case utils.OriginID:
@@ -337,7 +330,6 @@ func (fsev FSEvent) ParseEventValue(rsrFld *utils.RSRField, timezone string) str
func (fsev FSEvent) AsCDR(timezone string) *engine.CDR {
storCdr := new(engine.CDR)
storCdr.CGRID = fsev.GetCgrId(timezone)
storCdr.ToR = utils.VOICE
storCdr.OriginID = fsev.GetUUID()
storCdr.OriginHost = fsev.GetOriginatorIP(utils.META_DEFAULT)
@@ -356,18 +348,9 @@ func (fsev FSEvent) AsCDR(timezone string) *engine.CDR {
return storCdr
}
func (fsev FSEvent) ComputeLcr() bool {
if computeLcr, err := strconv.ParseBool(fsev[VAR_CGR_CMPUTELCR]); err != nil {
return false
} else {
return computeLcr
}
}
// Used with RLs
func (fsev FSEvent) AsMapStringInterface(timezone string) map[string]interface{} {
mp := make(map[string]interface{})
mp[utils.CGRID] = fsev.GetCgrId(timezone)
mp[utils.TOR] = utils.VOICE
mp[utils.OriginID] = fsev.GetUUID()
mp[utils.OriginHost] = fsev.GetOriginatorIP(utils.META_DEFAULT)
@@ -389,23 +372,6 @@ func (fsev FSEvent) AsMapStringInterface(timezone string) map[string]interface{}
return mp
}
// Converts into CallDescriptor due to responder interface needs
func (fsev FSEvent) AsCallDescriptor() (*engine.CallDescriptor, error) {
lcrReq := &engine.LcrRequest{
Direction: fsev.GetDirection(utils.META_DEFAULT),
Tenant: fsev.GetTenant(utils.META_DEFAULT),
Category: fsev.GetCategory(utils.META_DEFAULT),
Account: fsev.GetAccount(utils.META_DEFAULT),
Subject: fsev.GetSubject(utils.META_DEFAULT),
Destination: fsev.GetDestination(utils.META_DEFAULT),
SetupTime: utils.FirstNonEmpty(fsev[SETUP_TIME], fsev[ANSWER_TIME]),
Duration: fsev[DURATION],
ExtraFields: fsev.GetExtraFields(),
}
return lcrReq.AsCallDescriptor(config.CgrConfig().DefaultTimezone)
}
// V1AuthorizeArgs returns the arguments used in SMGv1.Authorize
func (fsev FSEvent) V1AuthorizeArgs() (args *sessionmanager.V1AuthorizeArgs) {
args = &sessionmanager.V1AuthorizeArgs{ // defaults
@@ -471,9 +437,11 @@ func (fsev FSEvent) V1TerminateSessionArgs() (args *sessionmanager.V1TerminateSe
Event: fsev.AsMapStringInterface(config.CgrConfig().DefaultTimezone),
},
}
subsystems, hasSubsystems := fsev[VarCGRSubsystems]
if (hasSubsystems && strings.Index(subsystems, SubSAccountS) == -1) ||
fsev[VarCGRSessionS] == "false" {
subsystems, has := fsev[VarCGRSubsystems]
if !has {
return
}
if strings.Index(subsystems, SubSAccountS) == -1 {
args.TerminateSession = false
}
if strings.Index(subsystems, SubSResourceS) != -1 {

View File

@@ -495,9 +495,6 @@ func TestParseEventValue(t *testing.T) {
cfg, _ := config.NewDefaultCGRConfig()
config.SetCgrConfig(cfg)
ev := NewFSEvent(hangupEv)
if cgrid := ev.ParseEventValue(&utils.RSRField{Id: utils.CGRID}, ""); cgrid != "164b0422fdc6a5117031b427439482c6a4f90e41" {
t.Error("Unexpected cgrid parsed", cgrid)
}
if tor := ev.ParseEventValue(&utils.RSRField{Id: utils.TOR}, ""); tor != utils.VOICE {
t.Error("Unexpected tor parsed", tor)
}
@@ -565,7 +562,7 @@ func TestFsEvAsCDR(t *testing.T) {
ev := NewFSEvent(hangupEv)
setupTime, _ := utils.ParseTimeDetectLayout("1436280728", "")
aTime, _ := utils.ParseTimeDetectLayout("1436280728", "")
eStoredCdr := &engine.CDR{CGRID: "164b0422fdc6a5117031b427439482c6a4f90e41",
eStoredCdr := &engine.CDR{
ToR: utils.VOICE, OriginID: "e3133bf7-dcde-4daf-9663-9a79ffcef5ad",
OriginHost: "10.0.3.15", Source: "FS_CHANNEL_HANGUP_COMPLETE", RequestType: utils.META_PREPAID,
Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001",

View File

@@ -437,7 +437,9 @@ func (self *CdrServer) rateCDR(cdr *CDR) ([]*CDR, error) {
}
return cdrsRated, nil
} else { //calculate CDR as for pseudoprepaid
utils.Logger.Warning(fmt.Sprintf("<Cdrs> WARNING: Could not find CallCostLog for cgrid: %s, source: %s, runid: %s, will recalculate", cdr.CGRID, utils.SESSION_MANAGER_SOURCE, cdr.RunID))
utils.Logger.Warning(
fmt.Sprintf("<Cdrs> WARNING: Could not find CallCostLog for cgrid: %s, source: %s, runid: %s, originID: %s, will recalculate",
cdr.CGRID, utils.SESSION_MANAGER_SOURCE, cdr.RunID, cdr.OriginID))
qryCC, err = self.getCostFromRater(cdr)
}
} else {

View File

@@ -47,6 +47,7 @@ const (
FS_PROGRESS_MEDIAMSEC = "progress_mediamsec"
FS_PROGRESSMS = "progressmsec"
FsUsername = "username"
FsIPv4 = "FreeSWITCH-IPv4"
)
func NewFSCdr(body []byte, cgrCfg *config.CGRConfig) (*FSCdr, error) {
@@ -71,9 +72,8 @@ type FSCdr struct {
body map[string]interface{} // keeps the loaded body for extra field search
}
func (fsCdr FSCdr) getCGRID(timezone string) string {
setupTime, _ := utils.ParseTimeDetectLayout(fsCdr.vars[FS_SETUP_TIME], timezone)
return utils.Sha1(fsCdr.vars[FS_UUID], setupTime.UTC().String())
func (fsCdr FSCdr) getCGRID() string {
return utils.Sha1(fsCdr.vars[FS_UUID], fsCdr.vars[FsIPv4])
}
func (fsCdr FSCdr) getExtraFields() map[string]string {
@@ -136,7 +136,7 @@ func (fsCdr FSCdr) firstDefined(fldNames []string, dfltFld string) (val string)
func (fsCdr FSCdr) AsCDR(timezone string) *CDR {
storCdr := new(CDR)
storCdr.CGRID = fsCdr.getCGRID(timezone)
storCdr.CGRID = fsCdr.getCGRID()
storCdr.ToR = utils.VOICE
storCdr.OriginID = fsCdr.vars[FS_UUID]
storCdr.OriginHost = fsCdr.vars[FS_IP]

View File

@@ -418,7 +418,7 @@ func TestFsCdrCDRFields(t *testing.T) {
setupTime, _ := utils.ParseTimeDetectLayout("1515666344", "")
answerTime, _ := utils.ParseTimeDetectLayout("1515666347", "")
expctCDR := &CDR{
CGRID: "e4618356eb77efdd5eee315e5b351f9eb2822569",
CGRID: "24b5766be325fa751fab5a0a06373e106f33a257",
ToR: utils.VOICE, OriginID: "3da8bf84-c133-4959-9e24-e72875cb33a1",
OriginHost: "10.10.10.204", Source: "freeswitch_json", Category: "call",
RequestType: utils.META_RATED, Tenant: "cgrates.org",

File diff suppressed because one or more lines are too long

View File

@@ -137,16 +137,14 @@ func (self SMGenericEvent) GetSetupTime(fieldName, timezone string) (time.Time,
if fieldName == utils.META_DEFAULT {
fieldName = utils.SetupTime
}
result, _ := utils.CastFieldIfToString(self[fieldName])
return utils.ParseTimeDetectLayout(result, timezone)
return utils.IfaceAsTime(self[fieldName], timezone)
}
func (self SMGenericEvent) GetAnswerTime(fieldName, timezone string) (time.Time, error) {
if fieldName == utils.META_DEFAULT {
fieldName = utils.AnswerTime
}
result, _ := utils.CastFieldIfToString(self[fieldName])
return utils.ParseTimeDetectLayout(result, timezone)
return utils.IfaceAsTime(self[fieldName], timezone)
}
func (self SMGenericEvent) GetEndTime(fieldName, timezone string) (time.Time, error) {

View File

@@ -399,6 +399,14 @@ func (smg *SMGeneric) sessionStart(evStart SMGenericEvent,
return nil, nil
}
stopDebitChan := make(chan struct{})
if len(sessionRuns) == 0 { // no sessions need to be created, this will be a placeholder for further updates
s := &SMGSession{CGRID: cgrID, EventStart: evStart,
RunID: utils.META_NONE, Timezone: smg.Timezone,
rals: smg.rals, cdrsrv: smg.cdrsrv,
clntConn: clntConn}
smg.recordASession(s)
return nil, nil
}
for _, sessionRun := range sessionRuns {
s := &SMGSession{CGRID: cgrID, EventStart: evStart,
RunID: sessionRun.DerivedCharger.RunID, Timezone: smg.Timezone,
@@ -429,6 +437,9 @@ func (smg *SMGeneric) sessionEnd(cgrID 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[cgrID] {
if s.RunID == utils.META_NONE {
continue
}
s.TotalUsage = usage // save final usage as totalUsage
if idx == 0 && s.stopDebit != nil {
close(s.stopDebit) // Stop automatic debits
@@ -767,6 +778,10 @@ func (smg *SMGeneric) UpdateSession(gev SMGenericEvent, clnt rpcclient.RpcClient
}
defer smg.replicateSessionsWithID(gev.GetCGRID(utils.META_DEFAULT), false, smg.smgReplConns)
for _, s := range aSessions[cgrID] {
if s.RunID == utils.META_NONE {
maxUsage = time.Duration(-1 * time.Second)
continue
}
var maxDur time.Duration
if maxDur, err = s.debit(maxUsage, lastUsed); err != nil {
return

View File

@@ -83,7 +83,7 @@ func StringToInterface(s string) interface{} {
return s
}
// ReflectFieldInterface parses intf attepting to return the field as string or error otherwise
// ReflectFieldInterface parses intf attepting to return the field value or error otherwise
// Supports "ExtraFields" where additional fields are dynamically inserted in map with field name: extraFieldsLabel
func ReflectFieldInterface(intf interface{}, fldName, extraFieldsLabel string) (retIf interface{}, err error) {
v := reflect.ValueOf(intf)
@@ -146,6 +146,18 @@ func ReflectFieldAsString(intf interface{}, fldName, extraFieldsLabel string) (s
}
}
func IfaceAsTime(itm interface{}, timezone string) (t time.Time, err error) {
switch itm.(type) {
case time.Time:
return itm.(time.Time), nil
case string:
return ParseTimeDetectLayout(itm.(string), timezone)
default:
err = fmt.Errorf("cannot convert field: %+v to time.Time", itm)
}
return
}
// AsMapStringIface converts an item (mostly struct) as map[string]interface{}
func AsMapStringIface(item interface{}) (map[string]interface{}, error) {
out := make(map[string]interface{})