mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
FreeSWITCHAgent with operational *prepaid calls, SessionS with support of Sessions with RunID: *none
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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{})
|
||||
|
||||
Reference in New Issue
Block a user