From 821b78de6038290d730c6b1ed24db8c5b45cf82b Mon Sep 17 00:00:00 2001 From: DanB Date: Tue, 25 Sep 2018 16:22:29 +0200 Subject: [PATCH] Diameter AgentRequest with support for config field of type=*template --- agents/agentreq.go | 28 +++++++++++++++++++++++++++- agents/agentreq_test.go | 2 +- agents/dmtagent.go | 3 ++- agents/httpagent.go | 2 +- agents/librad_test.go | 6 +++--- agents/radagent.go | 6 ++++-- config/daconfig.go | 12 ++++++------ config/libconfig_json.go | 2 +- data/conf/samples/dmtagent/data.json | 15 +++++++++++++++ utils/consts.go | 1 + 10 files changed, 61 insertions(+), 16 deletions(-) diff --git a/agents/agentreq.go b/agents/agentreq.go index 1ff54b4fe..6b7789da7 100644 --- a/agents/agentreq.go +++ b/agents/agentreq.go @@ -29,7 +29,8 @@ import ( func newAgentRequest(req config.DataProvider, vars map[string]interface{}, tntTpl config.RSRParsers, - dfltTenant string, filterS *engine.FilterS) (ar *AgentRequest) { + dfltTenant string, filterS *engine.FilterS, + templates map[string][]*config.FCTemplate) (ar *AgentRequest) { ar = &AgentRequest{ Request: req, Vars: config.NewNavigableMap(vars), @@ -37,6 +38,7 @@ func newAgentRequest(req config.DataProvider, vars map[string]interface{}, CGRReply: config.NewNavigableMap(nil), Reply: config.NewNavigableMap(nil), filterS: filterS, + templates: templates, } // populate tenant if tntIf, err := ar.ParseField( @@ -59,6 +61,7 @@ type AgentRequest struct { CGRReply *config.NavigableMap Reply *config.NavigableMap filterS *engine.FilterS + templates map[string][]*config.FCTemplate } // String implements engine.DataProvider @@ -106,6 +109,29 @@ func (ar *AgentRequest) FieldAsString(fldPath []string) (val string, err error) func (ar *AgentRequest) AsNavigableMap(tplFlds []*config.FCTemplate) ( nM *config.NavigableMap, err error) { nM = config.NewNavigableMap(nil) + // expand template fields + for i := 0; i < len(tplFlds); { + if tplFlds[i].Type == utils.MetaTemplate { + tplId, err := tplFlds[i].Value.ParseDataProvider(ar, utils.NestingSep) + if err != nil { + return nil, err + } + refTpl, has := ar.templates[tplId] + if !has { + return nil, fmt.Errorf("no template with id: <%s>", tplId) + } else if len(refTpl) == 0 { + continue + } + wrkSlice := make([]*config.FCTemplate, len(refTpl)+len(tplFlds[i:])-1) // so we can cover tplFlds[i+1:] + copy(wrkSlice[:len(refTpl)], refTpl) // copy fields out of referenced template + if len(tplFlds[i:]) > 1 { // copy the rest of the fields after MetaTemplate + copy(wrkSlice[len(refTpl):], tplFlds[i+1:]) + } + tplFlds = append(tplFlds[:i], wrkSlice...) // append the work + continue // don't increase index so we can recheck + } + i++ + } for _, tplFld := range tplFlds { if pass, err := ar.filterS.Pass(ar.Tenant, tplFld.Filters, ar); err != nil { diff --git a/agents/agentreq_test.go b/agents/agentreq_test.go index 6c3603f75..046508e6b 100644 --- a/agents/agentreq_test.go +++ b/agents/agentreq_test.go @@ -34,7 +34,7 @@ func TestAgReqAsNavigableMap(t *testing.T) { cfg, _ := config.NewDefaultCGRConfig() filterS := engine.NewFilterS(cfg, nil, dm) agReq := newAgentRequest(nil, nil, nil, - "cgrates.org", filterS) + "cgrates.org", filterS, nil) // populate request, emulating the way will be done in HTTPAgent agReq.CGRRequest.Set([]string{utils.CGRID}, utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), false) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index 0a58346b8..c3a6ca1d0 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -111,7 +111,8 @@ func (da *DiameterAgent) handleMessage(c diam.Conn, m *diam.Message) { reqProcessor, newAgentRequest( newDADataProvider(m), reqVars, - reqProcessor.Tenant, da.cgrCfg.DefaultTenant, da.filterS), + reqProcessor.Tenant, da.cgrCfg.DefaultTenant, da.filterS, + da.cgrCfg.DiameterAgentCfg().Templates), rply) if lclProcessed { processed = lclProcessed diff --git a/agents/httpagent.go b/agents/httpagent.go index 50c769e8d..2775099fe 100644 --- a/agents/httpagent.go +++ b/agents/httpagent.go @@ -62,7 +62,7 @@ func (ha *HTTPAgent) ServeHTTP(w http.ResponseWriter, req *http.Request) { utils.HTTPAgent, err.Error())) return } - agReq := newAgentRequest(dcdr, nil, ha.tenantCfg, ha.dfltTenant, ha.filterS) + agReq := newAgentRequest(dcdr, nil, ha.tenantCfg, ha.dfltTenant, ha.filterS, nil) var processed bool for _, reqProcessor := range ha.reqProcessors { var lclProcessed bool diff --git a/agents/librad_test.go b/agents/librad_test.go index 523d71cea..370a31917 100644 --- a/agents/librad_test.go +++ b/agents/librad_test.go @@ -97,7 +97,7 @@ func TestRadComposedFieldValue(t *testing.T) { if err := pkt.AddAVPWithName("Cisco-NAS-Port", "CGR1", "Cisco"); err != nil { t.Error(err) } - agReq := newAgentRequest(nil, nil, nil, "cgrates.org", nil) + agReq := newAgentRequest(nil, nil, nil, "cgrates.org", nil, nil) agReq.Vars.Set([]string{MetaRadReqType}, MetaRadAcctStart, false) agReq.Vars.Set([]string{"Cisco"}, "CGR1", false) agReq.Vars.Set([]string{"User-Name"}, "flopsy", false) @@ -117,7 +117,7 @@ func TestRadFieldOutVal(t *testing.T) { t.Error(err) } eOut := fmt.Sprintf("%s|flopsy|CGR1", MetaRadAcctStart) - agReq := newAgentRequest(nil, nil, nil, "cgrates.org", nil) + agReq := newAgentRequest(nil, nil, nil, "cgrates.org", nil, nil) agReq.Vars.Set([]string{MetaRadReqType}, MetaRadAcctStart, false) agReq.Vars.Set([]string{"Cisco"}, "CGR1", false) agReq.Vars.Set([]string{"User-Name"}, "flopsy", false) @@ -139,7 +139,7 @@ func TestRadReplyAppendAttributes(t *testing.T) { &config.FCTemplate{Tag: "Acct-Session-Time", FieldId: "Acct-Session-Time", Type: utils.META_COMPOSED, Value: config.NewRSRParsersMustCompile("~*cgrep.MaxUsage{*duration_seconds}", true)}, } - agReq := newAgentRequest(nil, nil, nil, "cgrates.org", nil) + agReq := newAgentRequest(nil, nil, nil, "cgrates.org", nil, 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) diff --git a/agents/radagent.go b/agents/radagent.go index 378b27b27..5b30a1429 100644 --- a/agents/radagent.go +++ b/agents/radagent.go @@ -83,7 +83,8 @@ func (ra *RadiusAgent) handleAuth(req *radigo.Packet) (rpl *radigo.Packet, err e rpl.Code = radigo.AccessAccept var processed bool for _, reqProcessor := range ra.cgrCfg.RadiusAgentCfg().RequestProcessors { - agReq := newAgentRequest(dcdr, nil, reqProcessor.Tenant, ra.cgrCfg.DefaultTenant, ra.filterS) + agReq := newAgentRequest(dcdr, nil, reqProcessor.Tenant, + ra.cgrCfg.DefaultTenant, ra.filterS, nil) agReq.Vars.Set([]string{MetaRadReqType}, utils.StringToInterface(MetaRadAuth), true) var lclProcessed bool if lclProcessed, err = ra.processRequest(reqProcessor, agReq, rpl); lclProcessed { @@ -121,7 +122,8 @@ func (ra *RadiusAgent) handleAcct(req *radigo.Packet) (rpl *radigo.Packet, err e rpl.Code = radigo.AccountingResponse var processed bool for _, reqProcessor := range ra.cgrCfg.RadiusAgentCfg().RequestProcessors { - agReq := newAgentRequest(dcdr, nil, reqProcessor.Tenant, ra.cgrCfg.DefaultTenant, ra.filterS) + agReq := newAgentRequest(dcdr, nil, reqProcessor.Tenant, + ra.cgrCfg.DefaultTenant, ra.filterS, nil) var lclProcessed bool if lclProcessed, err = ra.processRequest(reqProcessor, agReq, rpl); lclProcessed { processed = lclProcessed diff --git a/config/daconfig.go b/config/daconfig.go index 96657f013..15dd442b7 100644 --- a/config/daconfig.go +++ b/config/daconfig.go @@ -31,7 +31,7 @@ type DiameterAgentCfg struct { OriginRealm string VendorId int ProductName string - MessageTemplates map[string][]*FCTemplate + Templates map[string][]*FCTemplate RequestProcessors []*DARequestProcessor } @@ -67,12 +67,12 @@ func (da *DiameterAgentCfg) loadFromJsonCfg(jsnCfg *DiameterAgentJsonCfg) (err e if jsnCfg.Product_name != nil { da.ProductName = *jsnCfg.Product_name } - if jsnCfg.Message_templates != nil { - if da.MessageTemplates == nil { - da.MessageTemplates = make(map[string][]*FCTemplate) + if jsnCfg.Templates != nil { + if da.Templates == nil { + da.Templates = make(map[string][]*FCTemplate) } - for k, jsnTpls := range jsnCfg.Message_templates { - if da.MessageTemplates[k], err = FCTemplatesFromFCTemplatesJsonCfg(jsnTpls); err != nil { + for k, jsnTpls := range jsnCfg.Templates { + if da.Templates[k], err = FCTemplatesFromFCTemplatesJsonCfg(jsnTpls); err != nil { return } } diff --git a/config/libconfig_json.go b/config/libconfig_json.go index 028845481..d4119bb66 100755 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -335,7 +335,7 @@ type DiameterAgentJsonCfg struct { Origin_realm *string Vendor_id *int Product_name *string - Message_templates map[string][]*FcTemplateJsonCfg + Templates map[string][]*FcTemplateJsonCfg Request_processors *[]*DARequestProcessorJsnCfg } diff --git a/data/conf/samples/dmtagent/data.json b/data/conf/samples/dmtagent/data.json index b3dba2295..8cfc07a9b 100644 --- a/data/conf/samples/dmtagent/data.json +++ b/data/conf/samples/dmtagent/data.json @@ -8,6 +8,21 @@ "dry_run": false, // do not send the events to SMG, just log them "request_filter": "Service-Context-Id(^gprs);CC-Request-Type(1)", // filter requests processed by this processor "continue_on_success": false, // continue to the next template if executed + "templates":{ + "*cca": [ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value + {"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*data", "mandatory": true}, + {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Session-Id", "mandatory": true}, + {"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "^*prepaid", "mandatory": true}, + {"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true}, + {"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "^cgrates.org", "mandatory": true}, + {"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^generic", "mandatory": true}, + {"tag": "Account", "field_id": "Account", "type": "*grouped", "value": "Subscription-Id>Subscription-Id-Data", "field_filter":"Subscription-Id>Subscription-Id-Type(0)", "mandatory": true}, + {"tag": "Destination", "field_id": "Destination", "type": "*constant", "value": "data"}, + {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, + {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, + {"tag": "Usage", "field_id": "Usage", "type": "*constant", "value": "2048"}, + ] + } "ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value {"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*data", "mandatory": true}, {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Session-Id", "mandatory": true}, diff --git a/utils/consts.go b/utils/consts.go index 89874ba90..804dd1dac 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -522,6 +522,7 @@ const ( PipeSep = "|" MetaApp = "*app" MetaCmd = "*cmd" + MetaTemplate = "*template" ) // Migrator Action