diff --git a/agents/agentreq.go b/agents/agentreq.go index 718c7df3b..b63d6b72e 100644 --- a/agents/agentreq.go +++ b/agents/agentreq.go @@ -151,6 +151,23 @@ func (aReq *AgentRequest) ParseField( case utils.META_COMPOSED: out = aReq.composedField(cfgFld.Value) isString = true + case utils.META_USAGE_DIFFERENCE: + if len(cfgFld.Value) != 2 { + return nil, fmt.Errorf("invalid arguments <%s>", utils.ToJSON(cfgFld.Value)) + } else { + strVal1, err := aReq.FieldAsString(strings.Split(cfgFld.Value[0].Id, utils.NestingSep)) + strVal2, err := aReq.FieldAsString(strings.Split(cfgFld.Value[1].Id, utils.NestingSep)) + tEnd, err := utils.ParseTimeDetectLayout(strVal1, cfgFld.Timezone) + if err != nil { + return "", err + } + tStart, err := utils.ParseTimeDetectLayout(strVal2, cfgFld.Timezone) + if err != nil { + return "", err + } + out = tEnd.Sub(tStart).String() + } + isString = true } if isString { // format the string additionally with fmtFieldWidth out, err = utils.FmtFieldWidth(cfgFld.Tag, out.(string), cfgFld.Width, @@ -172,17 +189,18 @@ func (ar *AgentRequest) composedField(outTpl utils.RSRFields) (outVal string) { } continue } + valStr, err := ar.FieldAsString(strings.Split(rsrTpl.Id, utils.NestingSep)) if err != nil { utils.Logger.Warning( - fmt.Sprintf("<%s> %s", - utils.HTTPAgent, err.Error())) + fmt.Sprintf("<%s> %s FieldAsString %s", + utils.AgentRequest, err.Error(), rsrTpl.Id)) continue } if parsed, err := rsrTpl.Parse(valStr); err != nil { utils.Logger.Warning( - fmt.Sprintf("<%s> %s", - utils.HTTPAgent, err.Error())) + fmt.Sprintf("<%s> %s Parse ", + utils.AgentRequest, err.Error(), valStr)) } else { outVal += parsed } diff --git a/agents/agentreq_test.go b/agents/agentreq_test.go index 8c859a2c0..4e3a7de9e 100644 --- a/agents/agentreq_test.go +++ b/agents/agentreq_test.go @@ -18,7 +18,6 @@ along with this program. If not, see package agents -/* import ( "reflect" "testing" @@ -121,8 +120,6 @@ func TestAgReqAsNavigableMap(t *testing.T) { if mpOut, err := agReq.AsNavigableMap(tplFlds); err != nil { t.Error(err) } else if !reflect.DeepEqual(eMp, mpOut) { - t.Errorf("expecting: %+v, received: %+v", - eMp.AsMapStringInterface(), mpOut.AsMapStringInterface()) + t.Errorf("expecting: %+v, received: %+v", eMp, mpOut) } } -*/ diff --git a/agents/libdmt.go b/agents/libdmt.go index 57ab7929d..7c21935b0 100644 --- a/agents/libdmt.go +++ b/agents/libdmt.go @@ -64,6 +64,153 @@ var ( ErrDiameterRatingFailed = errors.New("Diameter rating failed") ) +// processorVars will hold various variables using during request processing +// here so we can define methods on it +type processorVars map[string]interface{} + +// hasSubsystems will return true on single subsystem being present in processorVars +func (pv processorVars) hasSubsystems() (has bool) { + for _, k := range []string{utils.MetaAccounts, utils.MetaResources, + utils.MetaSuppliers, utils.MetaAttributes} { + if _, has = pv[k]; has { + return + } + } + return +} + +func (pv processorVars) hasVar(k string) (has bool) { + _, has = pv[k] + return +} + +// valAsInterface returns the string value for fldName +func (pv processorVars) valAsInterface(fldPath string) (val interface{}, err error) { + fldName := fldPath + if strings.HasPrefix(fldPath, utils.MetaCGRReply) { + fldName = utils.MetaCGRReply + } + if !pv.hasVar(fldName) { + err = errors.New("not found") + return + } + return engine.NewNavigableMap(pv).FieldAsInterface(strings.Split(fldPath, utils.HIERARCHY_SEP)) +} + +// valAsString returns the string value for fldName +// returns empty if fldName not found +func (pv processorVars) valAsString(fldPath string) (val string, err error) { + fldName := fldPath + if strings.HasPrefix(fldPath, utils.MetaCGRReply) { + fldName = utils.MetaCGRReply + } + if !pv.hasVar(fldName) { + return "", utils.ErrNotFoundNoCaps + } + return engine.NewNavigableMap(pv).FieldAsString(strings.Split(fldPath, utils.HIERARCHY_SEP)) +} + +// asV1AuthorizeArgs returns the arguments needed by SessionSv1.AuthorizeEvent +func (pv processorVars) asV1AuthorizeArgs(cgrEv *utils.CGREvent) (args *sessions.V1AuthorizeArgs) { + args = &sessions.V1AuthorizeArgs{ // defaults + GetMaxUsage: true, + CGREvent: *cgrEv, + } + if !pv.hasSubsystems() { + return + } + if !pv.hasVar(utils.MetaAccounts) { + args.GetMaxUsage = false + } + if pv.hasVar(utils.MetaResources) { + args.AuthorizeResources = true + } + if pv.hasVar(utils.MetaSuppliers) { + args.GetSuppliers = true + } + if pv.hasVar(utils.MetaAttributes) { + args.GetAttributes = true + } + return +} + +// asV1InitSessionArgs returns the arguments used in SessionSv1.InitSession +func (pv processorVars) asV1InitSessionArgs(cgrEv *utils.CGREvent) (args *sessions.V1InitSessionArgs) { + args = &sessions.V1InitSessionArgs{ // defaults + InitSession: true, + CGREvent: *cgrEv, + } + if !pv.hasSubsystems() { + return + } + if !pv.hasVar(utils.MetaAccounts) { + args.InitSession = false + } + if pv.hasVar(utils.MetaResources) { + args.AllocateResources = true + } + if pv.hasVar(utils.MetaAttributes) { + args.GetAttributes = true + } + return +} + +// asV1UpdateSessionArgs returns the arguments used in SessionSv1.InitSession +func (pv processorVars) asV1UpdateSessionArgs(cgrEv *utils.CGREvent) (args *sessions.V1UpdateSessionArgs) { + args = &sessions.V1UpdateSessionArgs{ // defaults + UpdateSession: true, + CGREvent: *cgrEv, + } + if !pv.hasSubsystems() { + return + } + if !pv.hasVar(utils.MetaAccounts) { + args.UpdateSession = false + } + if pv.hasVar(utils.MetaAttributes) { + args.GetAttributes = true + } + return +} + +// asV1TerminateSessionArgs returns the arguments used in SMGv1.TerminateSession +func (pv processorVars) asV1TerminateSessionArgs(cgrEv *utils.CGREvent) (args *sessions.V1TerminateSessionArgs) { + args = &sessions.V1TerminateSessionArgs{ // defaults + TerminateSession: true, + CGREvent: *cgrEv, + } + if !pv.hasSubsystems() { + return + } + if !pv.hasVar(utils.MetaAccounts) { + args.TerminateSession = false + } + if pv.hasVar(utils.MetaResources) { + args.ReleaseResources = true + } + return +} + +func (pv processorVars) asV1ProcessEventArgs(cgrEv *utils.CGREvent) (args *sessions.V1ProcessEventArgs) { + args = &sessions.V1ProcessEventArgs{ // defaults + Debit: true, + CGREvent: *cgrEv, + } + if !pv.hasSubsystems() { + return + } + if !pv.hasVar(utils.MetaAccounts) { + args.Debit = false + } + if pv.hasVar(utils.MetaResources) { + args.AllocateResources = true + } + if pv.hasVar(utils.MetaAttributes) { + args.GetAttributes = true + } + return +} + func loadDictionaries(dictsDir, componentId string) error { fi, err := os.Stat(dictsDir) if err != nil { diff --git a/agents/librad.go b/agents/librad.go index d45daf455..ea55daf08 100644 --- a/agents/librad.go +++ b/agents/librad.go @@ -19,15 +19,11 @@ along with this program. If not, see package agents import ( - "errors" "fmt" - "strconv" "strings" - "time" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" - "github.com/cgrates/cgrates/sessions" "github.com/cgrates/cgrates/utils" "github.com/cgrates/radigo" ) @@ -47,7 +43,7 @@ func attrVendorFromPath(path string) (attrName, vendorName string) { // radComposedFieldValue extracts the field value out of RADIUS packet // procVars have priority over packet variables func radComposedFieldValue(pkt *radigo.Packet, - procVars processorVars, outTpl utils.RSRFields) (outVal string) { + agReq *AgentRequest, outTpl utils.RSRFields) (outVal string) { for _, rsrTpl := range outTpl { if rsrTpl.IsStatic() { if parsed, err := rsrTpl.Parse(""); err != nil { @@ -59,13 +55,11 @@ func radComposedFieldValue(pkt *radigo.Packet, } continue } - if val, err := procVars.valAsString(rsrTpl.Id); err != nil { - if err.Error() != "not found" { - utils.Logger.Warning( - fmt.Sprintf("<%s> %s", - utils.RadiusAgent, err.Error())) - continue - } + if val, err := agReq.FieldAsString(strings.Split(rsrTpl.Id, utils.NestingSep)); err != nil { + utils.Logger.Warning( + fmt.Sprintf("<%s> %s", + utils.RadiusAgent, err.Error())) + continue } else { if parsed, err := rsrTpl.Parse(val); err != nil { utils.Logger.Warning( @@ -90,41 +84,8 @@ func radComposedFieldValue(pkt *radigo.Packet, return outVal } -// radMetaHandler handles *handler type in configuration fields -func radMetaHandler(pkt *radigo.Packet, procVars processorVars, - cfgFld *config.CfgCdrField, roundingDecimals int) (outVal string, err error) { - handlerArgs := strings.Split( - radComposedFieldValue(pkt, procVars, cfgFld.Value), utils.HandlerArgSep) - switch cfgFld.HandlerId { - case MetaUsageDifference: // expects tEnd|tStart in the composed val - if len(handlerArgs) != 2 { - return "", errors.New("unexpected number of arguments") - } - tEnd, err := utils.ParseTimeDetectLayout(handlerArgs[0], cfgFld.Timezone) - if err != nil { - return "", err - } - tStart, err := utils.ParseTimeDetectLayout(handlerArgs[1], cfgFld.Timezone) - if err != nil { - return "", err - } - return tEnd.Sub(tStart).String(), nil - case utils.MetaDurationSeconds: - if len(handlerArgs) != 1 { - return "", errors.New("unexpected number of arguments") - } - val, err := utils.ParseDurationWithNanosecs(handlerArgs[0]) - if err != nil { - return "", err - } - return strconv.FormatInt(int64(utils.Round(val.Seconds(), - roundingDecimals, utils.ROUNDING_MIDDLE)), 10), nil - } - return -} - // radFieldOutVal formats the field value retrieved from RADIUS packet -func radFieldOutVal(pkt *radigo.Packet, processorVars processorVars, +func radFieldOutVal(pkt *radigo.Packet, agReq *AgentRequest, cfgFld *config.CfgCdrField) (outVal string, err error) { // different output based on cgrFld.Type switch cfgFld.Type { @@ -134,12 +95,7 @@ func radFieldOutVal(pkt *radigo.Packet, processorVars processorVars, case utils.META_CONSTANT: outVal = cfgFld.Value.Id() case utils.META_COMPOSED: - outVal = radComposedFieldValue(pkt, processorVars, cfgFld.Value) - case utils.META_HANDLER: - if outVal, err = radMetaHandler(pkt, processorVars, cfgFld, - config.CgrConfig().RoundingDecimals); err != nil { - return "", err - } + outVal = radComposedFieldValue(pkt, agReq, cfgFld.Value) default: return "", fmt.Errorf("unsupported configuration field type: <%s>", cfgFld.Type) } @@ -150,20 +106,10 @@ func radFieldOutVal(pkt *radigo.Packet, processorVars processorVars, } // radReplyAppendAttributes appends attributes to a RADIUS reply based on predefined template -func radReplyAppendAttributes(reply *radigo.Packet, procVars map[string]interface{}, +func radReplyAppendAttributes(reply *radigo.Packet, agReq *AgentRequest, cfgFlds []*config.CfgCdrField) (err error) { for _, cfgFld := range cfgFlds { - passedAllFilters := true - for _, fldFilter := range cfgFld.FieldFilter { - if !radPassesFieldFilter(reply, procVars, fldFilter) { - passedAllFilters = false - break - } - } - if !passedAllFilters { - continue - } - fmtOut, err := radFieldOutVal(reply, procVars, cfgFld) + fmtOut, err := radFieldOutVal(reply, agReq, cfgFld) if err != nil { return err } @@ -191,12 +137,15 @@ func NewCGRReply(rply engine.NavigableMapper, return engine.NewNavigableMap(map[string]interface{}{ utils.Error: errRply.Error()}), nil } - mp, err = rply.AsNavigableMap(nil) - if err != nil { - return nil, err + mp = engine.NewNavigableMap(nil) + if rply != nil { + mp, err = rply.AsNavigableMap(nil) + if err != nil { + return nil, err + } } mp.Set([]string{utils.Error}, "", false) // enforce empty error - return mp, nil + return } // newRADataProvider constructs a DataProvider @@ -215,7 +164,7 @@ type radiusDP struct { // String is part of engine.DataProvider interface // when called, it will display the already parsed values out of cache func (pk *radiusDP) String() string { - return "" + return utils.ToJSON(pk) } // FieldAsInterface is part of engine.DataProvider interface @@ -228,6 +177,9 @@ func (pk *radiusDP) FieldAsInterface(fldPath []string) (data interface{}, err er return } err = nil // cancel previous err + if len(pk.req.AttributesWithName(fldPath[0], "")) != 0 { + data = pk.req.AttributesWithName(fldPath[0], "")[0].GetStringValue() + } pk.cache.Set(fldPath, data, false) return } diff --git a/agents/librad_test.go b/agents/librad_test.go index 89d0b7ed3..8a89d98f9 100644 --- a/agents/librad_test.go +++ b/agents/librad_test.go @@ -28,7 +28,6 @@ import ( "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" - "github.com/cgrates/cgrates/sessions" "github.com/cgrates/cgrates/utils" "github.com/cgrates/radigo" ) @@ -91,47 +90,6 @@ func TestAttrVendorFromPath(t *testing.T) { } } -func TestRadPassesFieldFilter(t *testing.T) { - pkt := radigo.NewPacket(radigo.AccountingRequest, 1, dictRad, coder, "CGRateS.org") - if err := pkt.AddAVPWithName("User-Name", "flopsy", ""); err != nil { - t.Error(err) - } - if err := pkt.AddAVPWithName("Cisco-NAS-Port", "CGR1", "Cisco"); err != nil { - t.Error(err) - } - if !radPassesFieldFilter(pkt, nil, nil) { - t.Error("not passing empty filter") - } - if !radPassesFieldFilter(pkt, nil, - utils.NewRSRFieldMustCompile("User-Name(flopsy)")) { - t.Error("not passing valid filter") - } - if radPassesFieldFilter(pkt, nil, - utils.NewRSRFieldMustCompile("User-Name(notmatching)")) { - t.Error("passing invalid filter value") - } - if !radPassesFieldFilter(pkt, nil, - utils.NewRSRFieldMustCompile("Cisco>Cisco-NAS-Port(CGR1)")) { - t.Error("not passing valid filter") - } - if radPassesFieldFilter(pkt, nil, - utils.NewRSRFieldMustCompile("Cisco>Cisco-NAS-Port(notmatching)")) { - t.Error("passing invalid filter value") - } - if !radPassesFieldFilter(pkt, processorVars{MetaRadReqType: MetaRadAuth}, - utils.NewRSRFieldMustCompile(fmt.Sprintf("%s(%s)", MetaRadReqType, MetaRadAuth))) { - t.Error("not passing valid filter") - } - if radPassesFieldFilter(pkt, processorVars{MetaRadReqType: MetaRadAcctStart}, - utils.NewRSRFieldMustCompile(fmt.Sprintf("%s(%s)", MetaRadReqType, MetaRadAuth))) { - t.Error("passing invalid filter") - } - if radPassesFieldFilter(pkt, nil, - utils.NewRSRFieldMustCompile("UnknownField(notmatching)")) { - t.Error("passing invalid filter value") - } -} - func TestRadComposedFieldValue(t *testing.T) { pkt := radigo.NewPacket(radigo.AccountingRequest, 1, dictRad, coder, "CGRateS.org") if err := pkt.AddAVPWithName("User-Name", "flopsy", ""); err != nil { @@ -140,10 +98,13 @@ func TestRadComposedFieldValue(t *testing.T) { if err := pkt.AddAVPWithName("Cisco-NAS-Port", "CGR1", "Cisco"); err != nil { t.Error(err) } - eOut := fmt.Sprintf("%s|flopsy|CGR1", MetaRadAcctStart) - if out := radComposedFieldValue(pkt, processorVars{MetaRadReqType: MetaRadAcctStart}, - utils.ParseRSRFieldsMustCompile(fmt.Sprintf("%s;^|;User-Name;^|;Cisco>Cisco-NAS-Port", - MetaRadReqType), utils.INFIELD_SEP)); out != eOut { + agReq := newAgentRequest(nil, nil, "cgrates.org", nil) + agReq.Vars.Set([]string{MetaRadReqType}, MetaRadAcctStart, false) + agReq.Vars.Set([]string{"Cisco"}, "CGR1", false) + agReq.Vars.Set([]string{"User-Name"}, "flopsy", false) + eOut := "*radAcctStart|flopsy|CGR1" + if out := radComposedFieldValue(pkt, agReq, + utils.ParseRSRFieldsMustCompile("*vars.*radReqType;^|;*vars.User-Name;^|;*vars.Cisco", utils.INFIELD_SEP)); out != eOut { t.Errorf("Expecting: <%s>, received: <%s>", eOut, out) } } @@ -157,263 +118,33 @@ func TestRadFieldOutVal(t *testing.T) { t.Error(err) } eOut := fmt.Sprintf("%s|flopsy|CGR1", MetaRadAcctStart) + agReq := newAgentRequest(nil, nil, "cgrates.org", nil) + agReq.Vars.Set([]string{MetaRadReqType}, MetaRadAcctStart, false) + agReq.Vars.Set([]string{"Cisco"}, "CGR1", false) + agReq.Vars.Set([]string{"User-Name"}, "flopsy", false) + //processorVars{MetaRadReqType: MetaRadAcctStart} cfgFld := &config.CfgCdrField{Tag: "ComposedTest", Type: utils.META_COMPOSED, FieldId: utils.Destination, - Value: utils.ParseRSRFieldsMustCompile(fmt.Sprintf("%s;^|;User-Name;^|;Cisco>Cisco-NAS-Port", MetaRadReqType), utils.INFIELD_SEP), Mandatory: true} - if outVal, err := radFieldOutVal(pkt, processorVars{MetaRadReqType: MetaRadAcctStart}, cfgFld); err != nil { + Value: utils.ParseRSRFieldsMustCompile("*vars.*radReqType;^|;*vars.User-Name;^|;*vars.Cisco", utils.INFIELD_SEP), Mandatory: true} + if outVal, err := radFieldOutVal(pkt, agReq, cfgFld); err != nil { t.Error(err) } else if outVal != eOut { t.Errorf("Expecting: <%s>, received: <%s>", eOut, outVal) } } -func TestRadReqAsCGREvent(t *testing.T) { - pkt := radigo.NewPacket(radigo.AccountingRequest, 1, dictRad, coder, "CGRateS.org") - // Sample minimal packet sent by Kamailio - if err := pkt.AddAVPWithName("Acct-Status-Type", "2", ""); err != nil { - t.Error(err) - } - if err := pkt.AddAVPWithName("Service-Type", "15", ""); err != nil { - t.Error(err) - } - if err := pkt.AddAVPWithName("Sip-Response-Code", "200", ""); err != nil { - t.Error(err) - } - if err := pkt.AddAVPWithName("Sip-Method", "8", ""); err != nil { - t.Error(err) - } - if err := pkt.AddAVPWithName("Event-Timestamp", "1497106119", ""); err != nil { - t.Error(err) - } - if err := pkt.AddAVPWithName("Sip-From-Tag", "75c2f57b", ""); err != nil { - t.Error(err) - } - if err := pkt.AddAVPWithName("Sip-To-Tag", "51585361", ""); err != nil { - t.Error(err) - } - if err := pkt.AddAVPWithName("Acct-Session-Id", "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0", ""); err != nil { - t.Error(err) - } - if err := pkt.AddAVPWithName("User-Name", "1001", ""); err != nil { - t.Error(err) - } - if err := pkt.AddAVPWithName("Called-Station-Id", "1002", ""); err != nil { - t.Error(err) - } - if err := pkt.AddAVPWithName("Ascend-User-Acct-Time", "1497106115", ""); err != nil { - t.Error(err) - } - if err := pkt.AddAVPWithName("NAS-Port-Id", "5060", ""); err != nil { - t.Error(err) - } - if err := pkt.AddAVPWithName("Acct-Delay-Time", "0", ""); err != nil { - t.Error(err) - } - if err := pkt.AddAVPWithName("NAS-IP-Address", "127.0.0.1", ""); err != nil { - t.Error(err) - } - - cfgFlds := []*config.CfgCdrField{ - &config.CfgCdrField{Tag: "TOR", FieldId: utils.ToR, Type: utils.META_CONSTANT, - Value: utils.ParseRSRFieldsMustCompile(utils.VOICE, utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "OriginID", FieldId: utils.OriginID, Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile("Acct-Session-Id;^-;Sip-From-Tag;^-;Sip-To-Tag", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "OriginHost", FieldId: utils.OriginHost, Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile("NAS-IP-Address", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "RequestType", FieldId: utils.RequestType, Type: utils.META_CONSTANT, - Value: utils.ParseRSRFieldsMustCompile(utils.META_PREPAID, utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Direction", FieldId: utils.Direction, Type: utils.META_CONSTANT, - Value: utils.ParseRSRFieldsMustCompile(utils.OUT, utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Tenant", FieldId: utils.Tenant, Type: utils.META_CONSTANT, - Value: utils.ParseRSRFieldsMustCompile("cgrates.org", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Category", FieldId: utils.Category, Type: utils.META_CONSTANT, - Value: utils.ParseRSRFieldsMustCompile("call", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Account", FieldId: utils.Account, Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile("User-Name", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Destination", FieldId: utils.Destination, Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile("Called-Station-Id", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "SetupTime", FieldId: utils.SetupTime, Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile("Ascend-User-Acct-Time", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "AnswerTime", FieldId: utils.AnswerTime, Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile("Ascend-User-Acct-Time", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Usage", FieldId: utils.Usage, Type: utils.META_HANDLER, HandlerId: MetaUsageDifference, - Value: utils.ParseRSRFieldsMustCompile("Event-Timestamp;^|;Ascend-User-Acct-Time", utils.INFIELD_SEP)}, - } - eOut := &utils.CGREvent{ - Tenant: "cgrates.org", - ID: utils.UUIDSha1Prefix(), - Time: utils.TimePointer(time.Now()), - Event: map[string]interface{}{ - utils.Account: "1001", - utils.AnswerTime: "1497106115", - utils.Category: "call", - utils.Destination: "1002", - utils.Direction: utils.META_OUT, - utils.OriginHost: "127.0.0.1", - utils.OriginID: "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0-75c2f57b-51585361", - utils.RequestType: "*prepaid", - utils.SetupTime: "1497106115", - utils.Tenant: "cgrates.org", - utils.ToR: "*voice", - utils.Usage: "4s", - }, - } - if outVal, err := radReqAsCGREvent(pkt, processorVars{MetaRadReqType: MetaRadAcctStart}, nil, cfgFlds); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(outVal.Tenant, eOut.Tenant) { - t.Errorf("Expecting: <%s>, received: <%s>", utils.ToJSON(eOut.Tenant), utils.ToJSON(outVal.Tenant)) - } else if !reflect.DeepEqual(outVal.Event, eOut.Event) { - t.Errorf("Expecting: <%s>, received: <%s>", utils.ToJSON(eOut.Event), utils.ToJSON(outVal.Event)) - } -} - -func TestPVAsV1AuthorizeArgs(t *testing.T) { - cgrEv := &utils.CGREvent{ - Tenant: "cgrates.org", - ID: utils.UUIDSha1Prefix(), - Time: utils.TimePointer(time.Now()), - Event: map[string]interface{}{ - utils.Account: "1001", - utils.AnswerTime: "1497106115", - utils.Category: "call", - utils.Destination: "1002", - utils.Direction: utils.META_OUT, - utils.OriginHost: "127.0.0.1", - utils.OriginID: "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0-75c2f57b-51585361", - utils.RequestType: "*prepaid", - utils.SetupTime: "1497106115", - utils.Tenant: "cgrates.org", - utils.ToR: "*voice", - utils.Usage: "4s", - }, - } - expected := &sessions.V1AuthorizeArgs{ - GetMaxUsage: true, - CGREvent: *cgrEv, - } - outVal := processorVars{MetaRadReqType: MetaRadAcctStart}.asV1AuthorizeArgs(cgrEv) - if !reflect.DeepEqual(expected, outVal) { - t.Errorf("Expecting: <%s>, received: <%s>", utils.ToJSON(expected), utils.ToJSON(outVal)) - } -} - -func TestPVAsV1InitSessionArgs(t *testing.T) { - cgrEv := &utils.CGREvent{ - Tenant: "cgrates.org", - ID: utils.UUIDSha1Prefix(), - Time: utils.TimePointer(time.Now()), - Event: map[string]interface{}{ - utils.Account: "1001", - utils.AnswerTime: "1497106115", - utils.Category: "call", - utils.Destination: "1002", - utils.Direction: utils.META_OUT, - utils.OriginHost: "127.0.0.1", - utils.OriginID: "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0-75c2f57b-51585361", - utils.RequestType: "*prepaid", - utils.SetupTime: "1497106115", - utils.Tenant: "cgrates.org", - utils.ToR: "*voice", - utils.Usage: "4s", - }, - } - expected := &sessions.V1InitSessionArgs{ - InitSession: true, - CGREvent: *cgrEv, - } - outVal := processorVars{MetaRadReqType: MetaRadAcctStart}.asV1InitSessionArgs(cgrEv) - if !reflect.DeepEqual(expected, outVal) { - t.Errorf("Expecting: <%s>, received: <%s>", utils.ToJSON(expected), utils.ToJSON(outVal)) - } - eInitArgs := &sessions.V1InitSessionArgs{ - InitSession: false, - AllocateResources: true, - GetAttributes: true, - CGREvent: *cgrEv, - } - initArgs := processorVars{MetaRadReqType: MetaRadAcctStart, utils.MetaResources: true, - utils.MetaAttributes: true}.asV1InitSessionArgs(cgrEv) - if !reflect.DeepEqual(eInitArgs, initArgs) { - t.Errorf("expecting: %+v, received: %+v", eInitArgs, initArgs) - } -} - -func TestPVAsV1UpdateSessionArgs(t *testing.T) { - cgrEv := &utils.CGREvent{ - Tenant: "cgrates.org", - ID: utils.UUIDSha1Prefix(), - Time: utils.TimePointer(time.Now()), - Event: map[string]interface{}{ - utils.Account: "1001", - utils.AnswerTime: "1497106115", - utils.Category: "call", - utils.Destination: "1002", - utils.Direction: utils.META_OUT, - utils.OriginHost: "127.0.0.1", - utils.OriginID: "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0-75c2f57b-51585361", - utils.RequestType: "*prepaid", - utils.SetupTime: "1497106115", - utils.Tenant: "cgrates.org", - utils.ToR: "*voice", - utils.Usage: "4s", - }, - } - expected := &sessions.V1UpdateSessionArgs{ - UpdateSession: true, - CGREvent: *cgrEv, - } - outVal := processorVars{MetaRadReqType: MetaRadAcctStart}.asV1UpdateSessionArgs(cgrEv) - if !reflect.DeepEqual(expected, outVal) { - t.Errorf("Expecting: <%s>, received: <%s>", utils.ToJSON(expected), utils.ToJSON(outVal)) - } -} - -func TestPVAsTerminateSessionArgs(t *testing.T) { - cgrEv := &utils.CGREvent{ - Tenant: "cgrates.org", - ID: utils.UUIDSha1Prefix(), - Time: utils.TimePointer(time.Now()), - Event: map[string]interface{}{ - utils.Account: "1001", - utils.AnswerTime: "1497106115", - utils.Category: "call", - utils.Destination: "1002", - utils.Direction: utils.META_OUT, - utils.OriginHost: "127.0.0.1", - utils.OriginID: "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0-75c2f57b-51585361", - utils.RequestType: "*prepaid", - utils.SetupTime: "1497106115", - utils.Tenant: "cgrates.org", - utils.ToR: "*voice", - utils.Usage: "4s", - }, - } - expected := &sessions.V1TerminateSessionArgs{ - TerminateSession: true, - CGREvent: *cgrEv, - } - outVal := processorVars{MetaRadReqType: MetaRadAcctStart}.asV1TerminateSessionArgs(cgrEv) - if !reflect.DeepEqual(expected, outVal) { - t.Errorf("Expecting: <%s>, received: <%s>", utils.ToJSON(expected), utils.ToJSON(outVal)) - } -} - func TestRadReplyAppendAttributes(t *testing.T) { rply := radigo.NewPacket(radigo.AccessRequest, 2, dictRad, coder, "CGRateS.org").Reply() rplyFlds := []*config.CfgCdrField{ &config.CfgCdrField{Tag: "ReplyCode", FieldId: MetaRadReplyCode, Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile("*cgrReply>Attributes>RadReply", utils.INFIELD_SEP)}, + Value: utils.ParseRSRFieldsMustCompile("*cgrReply.Attributes.RadReply", utils.INFIELD_SEP)}, &config.CfgCdrField{Tag: "Acct-Session-Time", FieldId: "Acct-Session-Time", Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile("*cgrReply>MaxUsage{*duration_seconds}", utils.INFIELD_SEP)}, + Value: utils.ParseRSRFieldsMustCompile("*cgrReply.MaxUsage{*duration_seconds}", utils.INFIELD_SEP)}, } - procVars := make(processorVars) - procVars[utils.MetaCGRReply] = map[string]interface{}{ - utils.CapAttributes: map[string]interface{}{ - "RadReply": "AccessAccept", - utils.Account: "1001", - }, - utils.CapMaxUsage: time.Duration(time.Hour), - } - if err := radReplyAppendAttributes(rply, procVars, rplyFlds); err != nil { + agReq := newAgentRequest(nil, nil, "cgrates.org", nil) + agReq.CGRReply.Set([]string{utils.CapMaxUsage}, time.Duration(time.Hour), false) + agReq.CGRReply.Set([]string{utils.CapAttributes, "RadReply"}, "AccessAccept", false) + agReq.CGRReply.Set([]string{utils.CapAttributes, utils.Account}, "1001", false) + if err := radReplyAppendAttributes(rply, agReq, rplyFlds); err != nil { t.Error(err) } if rply.Code != radigo.AccessAccept { diff --git a/agents/radagent.go b/agents/radagent.go index dd250e4f3..6fb34ee96 100644 --- a/agents/radagent.go +++ b/agents/radagent.go @@ -19,8 +19,8 @@ along with this program. If not, see package agents import ( + "errors" "fmt" - "strconv" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" @@ -88,6 +88,7 @@ func (ra *RadiusAgent) handleAuth(req *radigo.Packet) (rpl *radigo.Packet, err e return } agReq := newAgentRequest(dcdr, ra.tenantCfg, ra.cgrCfg.DefaultTenant, ra.filterS) + agReq.Vars.Set([]string{MetaRadReqType}, utils.StringToInterface(MetaRadAuth), true) rpl = req.Reply() rpl.Code = radigo.AccessAccept var processed bool @@ -101,12 +102,12 @@ func (ra *RadiusAgent) handleAuth(req *radigo.Packet) (rpl *radigo.Packet, err e } } if err != nil { - utils.Logger.Err(fmt.Sprintf("<%s> error: <%s> ignoring request: %s, process vars: %+v", - utils.RadiusAgent, err.Error(), utils.ToJSON(req), procVars)) + utils.Logger.Err(fmt.Sprintf("<%s> error: <%s> ignoring request: %s, agentRequest: %+v", + utils.RadiusAgent, err.Error(), utils.ToJSON(req), utils.ToJSON(agReq))) return nil, nil } else if !processed { - utils.Logger.Err(fmt.Sprintf("<%s> no request processor enabled, ignoring request %s, process vars: %+v", - utils.RadiusAgent, utils.ToJSON(req), procVars)) + utils.Logger.Err(fmt.Sprintf("<%s> no request processor enabled, ignoring request %s, agentRequest: %+v", + utils.RadiusAgent, utils.ToJSON(req), utils.ToJSON(agReq))) return nil, nil } return @@ -115,24 +116,21 @@ func (ra *RadiusAgent) handleAuth(req *radigo.Packet) (rpl *radigo.Packet, err e // handleAcct handles RADIUS Accounting request // supports: Acct-Status-Type = Start, Interim-Update, Stop func (ra *RadiusAgent) handleAcct(req *radigo.Packet) (rpl *radigo.Packet, err error) { - req.SetAVPValues() // populate string values in AVPs - procVars := make(processorVars) - if avps := req.AttributesWithName("Acct-Status-Type", ""); len(avps) != 0 { // populate accounting type - switch avps[0].GetStringValue() { // first AVP found will give out the type of accounting - case RadAcctStart: - procVars[MetaRadReqType] = MetaRadAcctStart - case RadAcctInterimUpdate: - procVars[MetaRadReqType] = MetaRadAcctUpdate - case RadAcctStop: - procVars[MetaRadReqType] = MetaRadAcctStop - } + req.SetAVPValues() // populate string values in AVPs + dcdr, err := newRADataProvider(req) // dcdr will provide information from request + if err != nil { + utils.Logger.Warning( + fmt.Sprintf("<%s> error creating decoder: %s", + utils.RadiusAgent, err.Error())) + return } + agReq := newAgentRequest(dcdr, ra.tenantCfg, ra.cgrCfg.DefaultTenant, ra.filterS) rpl = req.Reply() rpl.Code = radigo.AccountingResponse var processed bool for _, reqProcessor := range ra.cgrCfg.RadiusAgentCfg().RequestProcessors { var lclProcessed bool - if lclProcessed, err = ra.processRequest(reqProcessor, req, procVars, rpl); lclProcessed { + if lclProcessed, err = ra.processRequest(reqProcessor, agReq, rpl); lclProcessed { processed = lclProcessed } if err != nil || (lclProcessed && !reqProcessor.ContinueOnSuccess) { @@ -140,12 +138,12 @@ func (ra *RadiusAgent) handleAcct(req *radigo.Packet) (rpl *radigo.Packet, err e } } if err != nil { - utils.Logger.Err(fmt.Sprintf("<%s> error: <%s> ignoring request: %s, process vars: %+v", - utils.RadiusAgent, err.Error(), utils.ToJSON(req), procVars)) + utils.Logger.Err(fmt.Sprintf("<%s> error: <%s> ignoring request: %s, agentRequest: %+v", + utils.RadiusAgent, err.Error(), utils.ToJSON(req), utils.ToJSON(agReq))) return nil, nil } else if !processed { - utils.Logger.Err(fmt.Sprintf("<%s> no request processor enabled, ignoring request %s, process vars: %+v", - utils.RadiusAgent, utils.ToJSON(req), procVars)) + utils.Logger.Err(fmt.Sprintf("<%s> no request processor enabled, ignoring request %s, agentRequest: %+v", + utils.RadiusAgent, utils.ToJSON(req), utils.ToJSON(agReq))) return nil, nil } return @@ -178,7 +176,7 @@ func (ra *RadiusAgent) processRequest(reqProcessor *config.RARequestProcessor, case utils.MetaDryRun: utils.Logger.Info( fmt.Sprintf("<%s> DRY_RUN, processorID: %s, CGREvent: %s", - utils.HTTPAgent, reqProcessor.Id, utils.ToJSON(cgrEv))) + utils.RadiusAgent, reqProcessor.Id, utils.ToJSON(cgrEv))) case utils.MetaAuth: authArgs := sessions.NewV1AuthorizeArgs( reqProcessor.Flags.HasKey(utils.MetaAttributes), @@ -191,7 +189,7 @@ func (ra *RadiusAgent) processRequest(reqProcessor *config.RARequestProcessor, reqProcessor.Flags.HasKey(utils.MetaSuppliersEventCost), *cgrEv) var authReply sessions.V1AuthorizeReply - err = ha.sessionS.Call(utils.SessionSv1AuthorizeEvent, + err = ra.sessionS.Call(utils.SessionSv1AuthorizeEvent, authArgs, &authReply) if agReq.CGRReply, err = NewCGRReply(&authReply, err); err != nil { return @@ -204,7 +202,7 @@ func (ra *RadiusAgent) processRequest(reqProcessor *config.RARequestProcessor, reqProcessor.Flags.HasKey(utils.MetaThresholds), reqProcessor.Flags.HasKey(utils.MetaStats), *cgrEv) var initReply sessions.V1InitSessionReply - err = ha.sessionS.Call(utils.SessionSv1InitiateSession, + err = ra.sessionS.Call(utils.SessionSv1InitiateSession, initArgs, &initReply) if agReq.CGRReply, err = NewCGRReply(&initReply, err); err != nil { return @@ -214,7 +212,7 @@ func (ra *RadiusAgent) processRequest(reqProcessor *config.RARequestProcessor, reqProcessor.Flags.HasKey(utils.MetaAttributes), reqProcessor.Flags.HasKey(utils.MetaAccounts), *cgrEv) var updateReply sessions.V1UpdateSessionReply - err = ha.sessionS.Call(utils.SessionSv1UpdateSession, + err = ra.sessionS.Call(utils.SessionSv1UpdateSession, updateArgs, &updateReply) if agReq.CGRReply, err = NewCGRReply(&updateReply, err); err != nil { return @@ -226,7 +224,7 @@ func (ra *RadiusAgent) processRequest(reqProcessor *config.RARequestProcessor, reqProcessor.Flags.HasKey(utils.MetaThresholds), reqProcessor.Flags.HasKey(utils.MetaStats), *cgrEv) var tRply string - err = ha.sessionS.Call(utils.SessionSv1TerminateSession, + err = ra.sessionS.Call(utils.SessionSv1TerminateSession, terminateArgs, &tRply) if agReq.CGRReply, err = NewCGRReply(nil, err); err != nil { return @@ -237,7 +235,7 @@ func (ra *RadiusAgent) processRequest(reqProcessor *config.RARequestProcessor, reqProcessor.Flags.HasKey(utils.MetaAccounts), reqProcessor.Flags.HasKey(utils.MetaAttributes), *cgrEv) var eventRply sessions.V1ProcessEventReply - err = ha.sessionS.Call(utils.SessionSv1ProcessEvent, + err = ra.sessionS.Call(utils.SessionSv1ProcessEvent, evArgs, &eventRply) if utils.ErrHasPrefix(err, utils.RalsErrorPrfx) { cgrEv.Event[utils.Usage] = 0 // avoid further debits @@ -251,7 +249,7 @@ func (ra *RadiusAgent) processRequest(reqProcessor *config.RARequestProcessor, // separate request so we can capture the Terminate/Event also here if reqProcessor.Flags.HasKey(utils.MetaCDRs) { var rplyCDRs string - if err = ha.sessionS.Call(utils.SessionSv1ProcessCDR, + if err = ra.sessionS.Call(utils.SessionSv1ProcessCDR, *cgrEv, &rplyCDRs); err != nil { agReq.CGRReply.Set([]string{utils.Error}, err.Error(), false) } @@ -261,11 +259,13 @@ func (ra *RadiusAgent) processRequest(reqProcessor *config.RARequestProcessor, } else { agReq.Reply.Merge(nM) } - //update rply *radigo.Packet + if err := radReplyAppendAttributes(rply, agReq, reqProcessor.ReplyFields); err != nil { + return false, err + } if reqType == utils.MetaDryRun { utils.Logger.Info( - fmt.Sprintf("<%s> DRY_RUN, HTTP reply: %s", - utils.HTTPAgent, utils.ToJSON(rply))) + fmt.Sprintf("<%s> DRY_RUN, Radius reply: %s", + utils.RadiusAgent, utils.ToJSON(rply))) } return true, nil } diff --git a/agents/radagent_it_test.go b/agents/radagent_it_test.go index a7d3c9eaf..bb5be8d99 100644 --- a/agents/radagent_it_test.go +++ b/agents/radagent_it_test.go @@ -191,7 +191,7 @@ func TestRAitAcctStart(t *testing.T) { } // Make sure the sessin is managed by SMG var aSessions []*sessions.ActiveSession - if err := raRPC.Call("SMGenericV1.GetActiveSessions", + if err := raRPC.Call(utils.SessionSv1GetActiveSessions, map[string]string{utils.RunID: utils.META_DEFAULT, utils.OriginID: "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0-51585361-75c2f57b"}, &aSessions); err != nil { @@ -279,7 +279,7 @@ func TestRAitAcctStop(t *testing.T) { t.Errorf("Unexpected CDR CostSource received for CDR: %v", cdrs[0]) } if cdrs[0].Cost != 0.01 { - t.Errorf("Unexpected CDR Cost received for CDR: %v", cdrs[0]) + t.Errorf("Unexpected CDR Cost received for CDR: %v", cdrs[0].Cost) } } } diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go index 2e3171d8b..022e6d780 100644 --- a/cmd/cgr-engine/cgr-engine.go +++ b/cmd/cgr-engine/cgr-engine.go @@ -324,7 +324,7 @@ func startRadiusAgent(internalSMGChan chan rpcclient.RpcClientConnection, exitCh return } } - ra, err := agents.NewRadiusAgent(cfg, smgConn) + ra, err := agents.NewRadiusAgent(cfg, filterS, smgConn) if err != nil { utils.Logger.Err(fmt.Sprintf(" error: <%s>", err.Error())) exitChan <- true diff --git a/data/conf/samples/radagent/cgrates.json b/data/conf/samples/radagent/cgrates.json index 930b4d721..f255714f2 100644 --- a/data/conf/samples/radagent/cgrates.json +++ b/data/conf/samples/radagent/cgrates.json @@ -29,18 +29,6 @@ "rals": { "enabled": true, - "cdrstats_conns": [ - {"address": "*internal"} - ], - "pubsubs_conns": [ - {"address": "*internal"} - ], - "users_conns": [ - {"address": "*internal"} - ], - "aliases_conns": [ - {"address": "*internal"} - ], }, "scheduler": { @@ -52,26 +40,6 @@ "rals_conns": [ {"address": "*internal"} ], - "cdrstats_conns": [ - {"address": "*internal"} - ], -}, - -"cdrstats": { - "enabled": true, -}, - -"pubsubs": { - "enabled": true, // starts PubSub service: . -}, - -"aliases": { - "enabled": true, // starts Aliases service: . -}, - -"users": { - "enabled": true, - "indexes": ["SubscriberId"], }, "resources": { @@ -88,6 +56,18 @@ "sessions": { "enabled": true, + "attributes_conns": [ + {"address": "127.0.0.1:2012", "transport": "*json"} + ], + "cdrs_conns": [ + {"address": "127.0.0.1:2012", "transport": "*json"} + ], + "rals_conns": [ + {"address": "127.0.0.1:2012", "transport": "*json"} + ], + "resources_conns": [ + {"address": "127.0.0.1:2012", "transport": "*json"} + ], "debit_interval": "10s", }, @@ -99,70 +79,73 @@ "request_processors": [ { "id": "KamailioAuth", - "filters": ["*string:*request.request_type:*radAuth"], - "flags": ["*dryrun"], + "filters": ["*string:*vars.*radReqType:*radAuth"], + "flags": ["*auth", "*accounts",], + "continue_on_success": false, "request_fields":[ {"tag": "RequestType", "field_id": "RequestType", "type": "*constant", "value": "*prepaid", "mandatory": true}, {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", - "value": "Acct-Session-Id;^-;Sip-From-Tag", "mandatory": true}, + "value": "*request.Acct-Session-Id;^-;*request.Sip-From-Tag", "mandatory": true}, {"tag": "Account", "field_id": "Account", "type": "*composed", - "value": "User-Name", "mandatory": true}, + "value": "*request.User-Name", "mandatory": true}, {"tag": "Destination", "field_id": "Destination", "type": "*composed", - "value": "Called-Station-Id", "mandatory": true}, + "value": "*request.Called-Station-Id", "mandatory": true}, {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", - "value": "Event-Timestamp", "mandatory": true}, + "value": "*request.Event-Timestamp", "mandatory": true}, {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", - "value": "Event-Timestamp", "mandatory": true}, + "value": "*request.Event-Timestamp", "mandatory": true}, ], "reply_fields":[ {"tag": "MaxUsage", "field_id": "SIP-AVP", "type": "*composed", - "value": "^session_max_time#;*cgrReply>MaxUsage{*duration_seconds}", "mandatory": true}, + "value": "^session_max_time#;*cgrReply.MaxUsage{*duration_seconds}", "mandatory": true}, ], }, { "id": "KamailioAccountingStart", - "filters": ["*string:*request.request_type:*radAcctStart"], - "flags": ["*dryrun"], + "filters": ["*string:*request.Acct-Status-Type:Start"], + "flags": ["*initiate","*attributes","*resources","*accounts"], + "continue_on_success": false, "request_fields":[ {"tag": "RequestType", "field_id": "RequestType", "type": "*constant", "value": "*prepaid", "mandatory": true}, {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", - "value": "Acct-Session-Id;^-;Sip-From-Tag;^-;Sip-To-Tag", "mandatory": true}, + "value": "*request.Acct-Session-Id;^-;*request.Sip-From-Tag;^-;*request.Sip-To-Tag", "mandatory": true}, {"tag": "OriginHost", "field_id": "OriginHost", "type": "*composed", - "value": "NAS-IP-Address", "mandatory": true}, + "value": "*request.NAS-IP-Address", "mandatory": true}, {"tag": "Account", "field_id": "Account", "type": "*composed", - "value": "User-Name", "mandatory": true}, + "value": "*request.User-Name", "mandatory": true}, {"tag": "Destination", "field_id": "Destination", "type": "*composed", - "value": "Called-Station-Id", "mandatory": true}, + "value": "*request.Called-Station-Id", "mandatory": true}, {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", - "value": "Ascend-User-Acct-Time", "mandatory": true}, + "value": "*request.Ascend-User-Acct-Time", "mandatory": true}, {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", - "value": "Ascend-User-Acct-Time", "mandatory": true}, + "value": "*request.Ascend-User-Acct-Time", "mandatory": true}, ], "reply_fields":[], }, { "id": "KamailioAccountingStop", - "filters": ["*string:*request.request_type:*radAcctStop"], - "flags": ["*dryrun"], + "filters": ["*string:*request.Acct-Status-Type:Stop"], + "flags": ["*terminate","*resources","*accounts","*cdrs"], + "continue_on_success": false, "request_fields":[ {"tag": "RequestType", "field_id": "RequestType", "type": "*constant", "value": "*prepaid", "mandatory": true}, {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", - "value": "Acct-Session-Id;^-;Sip-From-Tag;^-;Sip-To-Tag", "mandatory": true}, + "value": "*request.Acct-Session-Id;^-;*request.Sip-From-Tag;^-;*request.Sip-To-Tag", "mandatory": true}, {"tag": "OriginHost", "field_id": "OriginHost", "type": "*composed", - "value": "NAS-IP-Address", "mandatory": true}, + "value": "*request.NAS-IP-Address", "mandatory": true}, {"tag": "Account", "field_id": "Account", "type": "*composed", - "value": "User-Name", "mandatory": true}, + "value": "*request.User-Name", "mandatory": true}, {"tag": "Destination", "field_id": "Destination", "type": "*composed", - "value": "Called-Station-Id", "mandatory": true}, + "value": "*request.Called-Station-Id", "mandatory": true}, {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", - "value": "Ascend-User-Acct-Time", "mandatory": true}, + "value": "*request.Ascend-User-Acct-Time", "mandatory": true}, {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", - "value": "Ascend-User-Acct-Time", "mandatory": true}, - {"tag": "Usage", "field_id": "Usage", "type": "*handler", "handler_id": "*usage_difference", - "value": "Event-Timestamp;^|;Ascend-User-Acct-Time", "mandatory": true}, + "value": "*request.Ascend-User-Acct-Time", "mandatory": true}, + {"tag": "Usage", "field_id": "Usage", "type": "*usage_difference", + "value": "*request.Event-Timestamp;*request.Ascend-User-Acct-Time", "mandatory": true}, ], "reply_fields":[], }, diff --git a/sessions/sessions.go b/sessions/sessions.go index 3da5feae9..783093867 100644 --- a/sessions/sessions.go +++ b/sessions/sessions.go @@ -1902,6 +1902,7 @@ func (smg *SMGeneric) BiRPCv1TerminateSession(clnt rpcclient.RpcClientConnection if !args.TerminateSession && !args.ReleaseResources { return utils.NewErrMandatoryIeMissing("subsystems") } + // if args.CGREvent.Tenant == "" { args.CGREvent.Tenant = smg.cgrCfg.DefaultTenant } diff --git a/utils/consts.go b/utils/consts.go index 53e4106c5..802749ae9 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -323,6 +323,7 @@ const ( TRIGGER_BALANCE_EXPIRED = "*balance_expired" HIERARCHY_SEP = ">" META_COMPOSED = "*composed" + META_USAGE_DIFFERENCE = "*usage_difference" MetaString = "*string" NegativePrefix = "!" MatchStartPrefix = "^" @@ -523,6 +524,7 @@ const ( MetaDryRun = "*dryrun" Event = "Event" EmptyString = "" + AgentRequest = "AgentRequest" ) // Migrator Action