Diameter AgentRequest with support for config field of type=*template

This commit is contained in:
DanB
2018-09-25 16:22:29 +02:00
parent c253a64648
commit 821b78de60
10 changed files with 61 additions and 16 deletions

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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
}

View File

@@ -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},

View File

@@ -522,6 +522,7 @@ const (
PipeSep = "|"
MetaApp = "*app"
MetaCmd = "*cmd"
MetaTemplate = "*template"
)
// Migrator Action