RunFilter in DerivedChargers, ParseEventValue accepting RSRField in FreeSWITCH event

This commit is contained in:
DanB
2014-07-04 20:05:27 +02:00
parent a014ef8401
commit 9b37e97cc2
12 changed files with 314 additions and 170 deletions

View File

@@ -250,7 +250,7 @@ func TestConfigFromFile(t *testing.T) {
eCfg.FreeswitchServer = "test"
eCfg.FreeswitchPass = "test"
eCfg.FreeswitchReconnects = 99
eCfg.DerivedChargers = utils.DerivedChargers{&utils.DerivedCharger{RunId: "test", ReqTypeField: "test", DirectionField: "test", TenantField: "test",
eCfg.DerivedChargers = utils.DerivedChargers{&utils.DerivedCharger{RunId: "test", RunFilter: "", ReqTypeField: "test", DirectionField: "test", TenantField: "test",
CategoryField: "test", AccountField: "test", SubjectField: "test", DestinationField: "test", SetupTimeField: "test", AnswerTimeField: "test", UsageField: "test"}}
eCfg.CombinedDerivedChargers = true
eCfg.HistoryAgentEnabled = true
@@ -264,7 +264,13 @@ func TestConfigFromFile(t *testing.T) {
eCfg.MailerFromAddr = "test"
if !reflect.DeepEqual(cfg, eCfg) {
t.Log(eCfg)
for _, eDC := range eCfg.DerivedChargers {
fmt.Printf("ExpectDerivedChargers: %+v\n", eDC)
}
t.Log(cfg)
for _, eDC := range cfg.DerivedChargers {
fmt.Printf("DerivedChargers: %+v\n", eDC)
}
t.Error("Loading of configuration from file failed!")
}
}
@@ -288,12 +294,6 @@ extra_fields = ~effective_caller_id_number:s/(\d+)/+$1/
t.Errorf("Unexpected value for config CdrsExtraFields: %v", cfg.CDRSExtraFields)
}
eFieldsCfg = []byte(`[cdrs]
extra_fields = extr1,extr2,
`)
if _, err := NewCGRConfigFromBytes(eFieldsCfg); err == nil {
t.Error("Failed to detect empty field in the end of extra fields defition")
}
eFieldsCfg = []byte(`[cdrs]
extra_fields = extr1,~extr2:s/x.+/
`)
if _, err := NewCGRConfigFromBytes(eFieldsCfg); err == nil {

View File

@@ -29,15 +29,11 @@ import (
// Adds support for slice values in config
func ConfigSlice(cfgVal string) ([]string, error) {
cfgValStrs := strings.Split(cfgVal, ",") // If need arrises, we can make the separator configurable
if len(cfgValStrs) == 1 && cfgValStrs[0] == "" { // Prevents returning iterable with empty value
return []string{}, nil
}
cfgValStrs := strings.Split(cfgVal, ",") // If need arrises, we can make the separator configurable
for idx, elm := range cfgValStrs {
if elm == "" { //One empty element is presented when splitting empty string
return nil, errors.New("Empty values in config slice")
}
//if elm == "" { //One empty element is presented when splitting empty string
// return nil, errors.New("Empty values in config slice")
//}
cfgValStrs[idx] = strings.TrimSpace(elm) // By default spaces are not removed so we do it here to avoid unpredicted results in config
}
return cfgValStrs, nil
@@ -50,10 +46,6 @@ func ParseRSRFields(configVal string) ([]*utils.RSRField, error) {
}
rsrFields := make([]*utils.RSRField, len(cfgValStrs))
for idx, cfgValStr := range cfgValStrs {
if len(cfgValStr) == 0 { //One empty element is presented when splitting empty string
return nil, errors.New("Empty values in config slice")
}
if rsrField, err := utils.NewRSRField(cfgValStr); err != nil {
return nil, err
} else {
@@ -65,11 +57,15 @@ func ParseRSRFields(configVal string) ([]*utils.RSRField, error) {
// Parse the configuration file and returns utils.DerivedChargers instance if no errors
func ParseCfgDerivedCharging(c *conf.ConfigFile) (dcs utils.DerivedChargers, err error) {
var runIds, reqTypeFlds, directionFlds, tenantFlds, torFlds, acntFlds, subjFlds, dstFlds, sTimeFlds, aTimeFlds, durFlds []string
var runIds, runFilters, reqTypeFlds, directionFlds, tenantFlds, torFlds, acntFlds, subjFlds, dstFlds, sTimeFlds, aTimeFlds, durFlds []string
cfgVal, _ := c.GetString("derived_charging", "run_ids")
if runIds, err = ConfigSlice(cfgVal); err != nil {
return nil, err
}
cfgVal, _ = c.GetString("derived_charging", "run_filters")
if runFilters, err = ConfigSlice(cfgVal); err != nil {
return nil, err
}
cfgVal, _ = c.GetString("derived_charging", "reqtype_fields")
if reqTypeFlds, err = ConfigSlice(cfgVal); err != nil {
return nil, err
@@ -111,7 +107,8 @@ func ParseCfgDerivedCharging(c *conf.ConfigFile) (dcs utils.DerivedChargers, err
return nil, err
}
// We need all to be the same length
if len(reqTypeFlds) != len(runIds) ||
if len(runFilters) != len(runIds) ||
len(reqTypeFlds) != len(runIds) ||
len(directionFlds) != len(runIds) ||
len(tenantFlds) != len(runIds) ||
len(torFlds) != len(runIds) ||
@@ -125,8 +122,11 @@ func ParseCfgDerivedCharging(c *conf.ConfigFile) (dcs utils.DerivedChargers, err
}
// Create the individual chargers and append them to the final instance
dcs = make(utils.DerivedChargers, 0)
if len(runIds) == 1 && len(runIds[0]) == 0 { // Avoid iterating on empty runid
return dcs, nil
}
for runIdx, runId := range runIds {
dc, err := utils.NewDerivedCharger(runId, reqTypeFlds[runIdx], directionFlds[runIdx], tenantFlds[runIdx], torFlds[runIdx],
dc, err := utils.NewDerivedCharger(runId, runFilters[runIdx], reqTypeFlds[runIdx], directionFlds[runIdx], tenantFlds[runIdx], torFlds[runIdx],
acntFlds[runIdx], subjFlds[runIdx], dstFlds[runIdx], sTimeFlds[runIdx], aTimeFlds[runIdx], durFlds[runIdx])
if err != nil {
return nil, err

View File

@@ -26,6 +26,15 @@ import (
"github.com/cgrates/cgrates/utils"
)
func TestConfigSlice(t *testing.T) {
eCS := []string{"", ""}
if cs, err := ConfigSlice(" , "); err != nil {
t.Error("Unexpected error: ", err)
} else if !reflect.DeepEqual(eCS, cs) {
t.Errorf("Expecting: %v, received: %v", eCS, cs)
}
}
func TestParseRSRFields(t *testing.T) {
fields := `host,~sip_redirected_to:s/sip:\+49(\d+)@/0$1/,destination`
expectParsedFields := []*utils.RSRField{&utils.RSRField{Id: "host"},
@@ -41,6 +50,7 @@ func TestParseRSRFields(t *testing.T) {
func TestParseCfgDerivedCharging(t *testing.T) {
eFieldsCfg := []byte(`[derived_charging]
run_ids = run1, run2
run_filters =,
reqtype_fields = test1, test2
direction_fields = test1, test2
tenant_fields = test1, test2

View File

@@ -100,6 +100,7 @@ reconnects = 99 # Number of attempts on connect failure.
[derived_charging]
run_ids = test # Identifiers of additional sessions control.
run_filters = # No filters applied
reqtype_fields = test # Name of request type fields to be used during additional sessions control <""|*default|field_name>.
direction_fields = test # Name of direction fields to be used during additional sessions control <""|*default|field_name>.
tenant_fields = test # Name of tenant fields to be used during additional sessions control <""|*default|field_name>.

View File

@@ -104,6 +104,7 @@
[derived_charging]
# run_ids = # Identifiers of additional sessions control.
# run_filters = # List of cdr field filters for each run.
# reqtype_fields = # Name of request type fields to be used during additional sessions control <""|*default|field_name>.
# direction_fields = # Name of direction fields to be used during additional sessions control <""|*default|field_name>.
# tenant_fields = # Name of tenant fields to be used during additional sessions control <""|*default|field_name>.

View File

@@ -120,6 +120,11 @@ func (self *Mediator) RateCdr(storedCdr *utils.StoredCdr) error {
return errors.New(errText)
}
for _, dc := range dcs {
dcRunFilter, _ := utils.NewRSRField(dc.RunFilter)
if dcRunFilter != nil && storedCdr.FieldAsString(&utils.RSRField{Id: dcRunFilter.Id}) != storedCdr.FieldAsString(dcRunFilter) {
engine.Logger.Info(fmt.Sprintf("Ignoring DerivedCharger with id %s due to non matching filter", dc.RunId))
continue
}
dcReqTypeFld, _ := utils.NewRSRField(dc.ReqTypeField)
dcDirFld, _ := utils.NewRSRField(dc.DirectionField)
dcTenantFld, _ := utils.NewRSRField(dc.TenantField)

View File

@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package sessionmanager
import (
"github.com/cgrates/cgrates/utils"
"time"
)
@@ -40,4 +41,5 @@ type Event interface {
GetEndTime() (time.Time, error)
GetDuration(string) (time.Duration, error)
MissingParameter() bool
ParseEventValue(*utils.RSRField) string
}

View File

@@ -20,6 +20,7 @@ package sessionmanager
import (
"fmt"
"strconv"
"strings"
"time"
@@ -203,3 +204,48 @@ func (fsev FSEvent) GetDuration(fieldName string) (dur time.Duration, err error)
}
return utils.ParseDurationWithSecs(durStr)
}
// Used in derived charging and sittuations when we need to run regexp on fields
func (fsev FSEvent) ParseEventValue(rsrFld *utils.RSRField) string {
switch rsrFld.Id {
case utils.CGRID:
return rsrFld.ParseValue(fsev.GetCgrId())
case utils.TOR:
return rsrFld.ParseValue(utils.VOICE)
case utils.ACCID:
return rsrFld.ParseValue(fsev.GetUUID())
case utils.CDRHOST:
return rsrFld.ParseValue(fsev["FreeSWITCH-IPv4"])
case utils.CDRSOURCE:
return rsrFld.ParseValue("FS_EVENT")
case utils.REQTYPE:
return rsrFld.ParseValue(fsev.GetReqType(""))
case utils.DIRECTION:
return rsrFld.ParseValue(fsev.GetDirection(""))
case utils.TENANT:
return rsrFld.ParseValue(fsev.GetTenant(""))
case utils.CATEGORY:
return rsrFld.ParseValue(fsev.GetCategory(""))
case utils.ACCOUNT:
return rsrFld.ParseValue(fsev.GetAccount(""))
case utils.SUBJECT:
return rsrFld.ParseValue(fsev.GetSubject(""))
case utils.DESTINATION:
return rsrFld.ParseValue(fsev.GetDestination(""))
case utils.SETUP_TIME:
st, _ := fsev.GetSetupTime("")
return rsrFld.ParseValue(st.String())
case utils.ANSWER_TIME:
at, _ := fsev.GetAnswerTime("")
return rsrFld.ParseValue(at.String())
case utils.USAGE:
dur, _ := fsev.GetDuration("")
return rsrFld.ParseValue(strconv.FormatInt(dur.Nanoseconds(), 10))
case utils.MEDI_RUNID:
return rsrFld.ParseValue(utils.DEFAULT_RUNID)
case utils.COST:
return rsrFld.ParseValue(strconv.FormatFloat(-1, 'f', -1, 64)) // Recommended to use FormatCost
default:
return rsrFld.ParseValue(fsev[rsrFld.Id])
}
}

View File

@@ -26,138 +26,7 @@ import (
"github.com/cgrates/cgrates/utils"
)
func TestEventCreation(t *testing.T) {
body := `Event-Name: RE_SCHEDULE
Core-UUID: 792e181c-b6e6-499c-82a1-52a778e7d82d
FreeSWITCH-Hostname: h1.ip-switch.net
FreeSWITCH-Switchname: h1.ip-switch.net
FreeSWITCH-IPv4: 88.198.12.156
FreeSWITCH-IPv6: %3A%3A1
Event-Date-Local: 2012-10-05%2013%3A41%3A38
Event-Date-GMT: Fri,%2005%20Oct%202012%2011%3A41%3A38%20GMT
Event-Date-Timestamp: 1349437298012866
Event-Calling-File: switch_scheduler.c
Event-Calling-Function: switch_scheduler_execute
Event-Calling-Line-Number: 65
Event-Sequence: 34263
Task-ID: 2
Task-Desc: heartbeat
Task-Group: core
Task-Runtime: 1349437318`
ev := new(FSEvent).New(body)
if ev.GetName() != "RE_SCHEDULE" {
t.Error("Event not parsed correctly: ", ev)
}
l := len(ev.(FSEvent))
if l != 17 {
t.Error("Incorrect number of event fields: ", l)
}
}
// Detects if any of the parsers do not return static values
func TestEventParseStatic(t *testing.T) {
ev := new(FSEvent).New("")
setupTime, _ := ev.GetSetupTime("^2013-12-07 08:42:24")
answerTime, _ := ev.GetAnswerTime("^2013-12-07 08:42:24")
dur, _ := ev.GetDuration("^60s")
if ev.GetReqType("^test") != "test" ||
ev.GetDirection("^test") != "test" ||
ev.GetTenant("^test") != "test" ||
ev.GetCategory("^test") != "test" ||
ev.GetAccount("^test") != "test" ||
ev.GetSubject("^test") != "test" ||
ev.GetDestination("^test") != "test" ||
setupTime != time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC) ||
answerTime != time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC) ||
dur != time.Duration(60)*time.Second {
t.Error("Values out of static not matching",
ev.GetReqType("^test") != "test",
ev.GetDirection("^test") != "test",
ev.GetTenant("^test") != "test",
ev.GetCategory("^test") != "test",
ev.GetAccount("^test") != "test",
ev.GetSubject("^test") != "test",
ev.GetDestination("^test") != "test",
setupTime != time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC),
answerTime != time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC),
dur != time.Duration(60)*time.Second)
}
}
// Test here if the answer is selected out of headers we specify, even if not default defined
func TestEventSelectiveHeaders(t *testing.T) {
body := `Event-Name: RE_SCHEDULE
Core-UUID: 792e181c-b6e6-499c-82a1-52a778e7d82d
FreeSWITCH-Hostname: h1.ip-switch.net
FreeSWITCH-Switchname: h1.ip-switch.net
FreeSWITCH-IPv4: 88.198.12.156
FreeSWITCH-IPv6: %3A%3A1
Event-Date-Local: 2012-10-05%2013%3A41%3A38
Event-Date-GMT: Fri,%2005%20Oct%202012%2011%3A41%3A38%20GMT
Event-Date-Timestamp: 1349437298012866
Event-Calling-File: switch_scheduler.c
Event-Calling-Function: switch_scheduler_execute
Event-Calling-Line-Number: 65
Event-Sequence: 34263
Task-ID: 2
Task-Desc: heartbeat
Task-Group: core
Task-Runtime: 1349437318`
cfg, _ = config.NewDefaultCGRConfig()
config.SetCgrConfig(cfg)
ev := new(FSEvent).New(body)
setupTime, _ := ev.GetSetupTime("Event-Date-Local")
answerTime, _ := ev.GetAnswerTime("Event-Date-Local")
dur, _ := ev.GetDuration("Event-Calling-Line-Number")
if ev.GetReqType("FreeSWITCH-Hostname") != "h1.ip-switch.net" ||
ev.GetDirection("FreeSWITCH-Hostname") != "*out" ||
ev.GetTenant("FreeSWITCH-Hostname") != "h1.ip-switch.net" ||
ev.GetCategory("FreeSWITCH-Hostname") != "h1.ip-switch.net" ||
ev.GetAccount("FreeSWITCH-Hostname") != "h1.ip-switch.net" ||
ev.GetSubject("FreeSWITCH-Hostname") != "h1.ip-switch.net" ||
ev.GetDestination("FreeSWITCH-Hostname") != "h1.ip-switch.net" ||
setupTime != time.Date(2012, 10, 5, 13, 41, 38, 0, time.UTC) ||
answerTime != time.Date(2012, 10, 5, 13, 41, 38, 0, time.UTC) ||
dur != time.Duration(65)*time.Second {
t.Error("Values out of static not matching",
ev.GetReqType("FreeSWITCH-Hostname") != "h1.ip-switch.net",
ev.GetDirection("FreeSWITCH-Hostname") != "*out",
ev.GetTenant("FreeSWITCH-Hostname") != "h1.ip-switch.net",
ev.GetCategory("FreeSWITCH-Hostname") != "h1.ip-switch.net",
ev.GetAccount("FreeSWITCH-Hostname") != "h1.ip-switch.net",
ev.GetSubject("FreeSWITCH-Hostname") != "h1.ip-switch.net",
ev.GetDestination("FreeSWITCH-Hostname") != "h1.ip-switch.net",
setupTime != time.Date(2012, 10, 5, 13, 41, 38, 0, time.UTC),
answerTime != time.Date(2012, 10, 5, 13, 41, 38, 0, time.UTC),
dur != time.Duration(65)*time.Second)
}
}
func TestDDazEmptyTime(t *testing.T) {
body := `Event-Name: RE_SCHEDULE
Core-UUID: 792e181c-b6e6-499c-82a1-52a778e7d82d
FreeSWITCH-Hostname: h1.ip-switch.net
FreeSWITCH-Switchname: h1.ip-switch.net
FreeSWITCH-IPv4: 88.198.12.156
Caller-Channel-Created-Time: 0
Caller-Channel-Answered-Time
Task-Runtime: 1349437318`
var nilTime time.Time
ev := new(FSEvent).New(body)
if setupTime, err := ev.GetSetupTime(""); err != nil {
t.Error("Error when parsing empty setupTime")
} else if setupTime != nilTime {
t.Error("Expecting nil time, got: ", setupTime)
}
if answerTime, err := ev.GetAnswerTime(""); err != nil {
t.Error("Error when parsing empty setupTime")
} else if answerTime != nilTime {
t.Error("Expecting nil time, got: ", answerTime)
}
}
func TestParseFsHangup(t *testing.T) {
hangupEv := `Event-Name: CHANNEL_HANGUP_COMPLETE
var hangupEv string = `Event-Name: CHANNEL_HANGUP_COMPLETE
Core-UUID: bb890f9e-0aae-476d-8292-91b434eb4f73
FreeSWITCH-Hostname: iPBXDev
FreeSWITCH-Switchname: iPBXDev
@@ -469,6 +338,138 @@ variable_rtp_audio_out_cng_packet_count: 0
variable_rtp_audio_rtcp_packet_count: 0
variable_rtp_audio_rtcp_octet_count: 0
`
func TestEventCreation(t *testing.T) {
body := `Event-Name: RE_SCHEDULE
Core-UUID: 792e181c-b6e6-499c-82a1-52a778e7d82d
FreeSWITCH-Hostname: h1.ip-switch.net
FreeSWITCH-Switchname: h1.ip-switch.net
FreeSWITCH-IPv4: 88.198.12.156
FreeSWITCH-IPv6: %3A%3A1
Event-Date-Local: 2012-10-05%2013%3A41%3A38
Event-Date-GMT: Fri,%2005%20Oct%202012%2011%3A41%3A38%20GMT
Event-Date-Timestamp: 1349437298012866
Event-Calling-File: switch_scheduler.c
Event-Calling-Function: switch_scheduler_execute
Event-Calling-Line-Number: 65
Event-Sequence: 34263
Task-ID: 2
Task-Desc: heartbeat
Task-Group: core
Task-Runtime: 1349437318`
ev := new(FSEvent).New(body)
if ev.GetName() != "RE_SCHEDULE" {
t.Error("Event not parsed correctly: ", ev)
}
l := len(ev.(FSEvent))
if l != 17 {
t.Error("Incorrect number of event fields: ", l)
}
}
// Detects if any of the parsers do not return static values
func TestEventParseStatic(t *testing.T) {
ev := new(FSEvent).New("")
setupTime, _ := ev.GetSetupTime("^2013-12-07 08:42:24")
answerTime, _ := ev.GetAnswerTime("^2013-12-07 08:42:24")
dur, _ := ev.GetDuration("^60s")
if ev.GetReqType("^test") != "test" ||
ev.GetDirection("^test") != "test" ||
ev.GetTenant("^test") != "test" ||
ev.GetCategory("^test") != "test" ||
ev.GetAccount("^test") != "test" ||
ev.GetSubject("^test") != "test" ||
ev.GetDestination("^test") != "test" ||
setupTime != time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC) ||
answerTime != time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC) ||
dur != time.Duration(60)*time.Second {
t.Error("Values out of static not matching",
ev.GetReqType("^test") != "test",
ev.GetDirection("^test") != "test",
ev.GetTenant("^test") != "test",
ev.GetCategory("^test") != "test",
ev.GetAccount("^test") != "test",
ev.GetSubject("^test") != "test",
ev.GetDestination("^test") != "test",
setupTime != time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC),
answerTime != time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC),
dur != time.Duration(60)*time.Second)
}
}
// Test here if the answer is selected out of headers we specify, even if not default defined
func TestEventSelectiveHeaders(t *testing.T) {
body := `Event-Name: RE_SCHEDULE
Core-UUID: 792e181c-b6e6-499c-82a1-52a778e7d82d
FreeSWITCH-Hostname: h1.ip-switch.net
FreeSWITCH-Switchname: h1.ip-switch.net
FreeSWITCH-IPv4: 88.198.12.156
FreeSWITCH-IPv6: %3A%3A1
Event-Date-Local: 2012-10-05%2013%3A41%3A38
Event-Date-GMT: Fri,%2005%20Oct%202012%2011%3A41%3A38%20GMT
Event-Date-Timestamp: 1349437298012866
Event-Calling-File: switch_scheduler.c
Event-Calling-Function: switch_scheduler_execute
Event-Calling-Line-Number: 65
Event-Sequence: 34263
Task-ID: 2
Task-Desc: heartbeat
Task-Group: core
Task-Runtime: 1349437318`
cfg, _ = config.NewDefaultCGRConfig()
config.SetCgrConfig(cfg)
ev := new(FSEvent).New(body)
setupTime, _ := ev.GetSetupTime("Event-Date-Local")
answerTime, _ := ev.GetAnswerTime("Event-Date-Local")
dur, _ := ev.GetDuration("Event-Calling-Line-Number")
if ev.GetReqType("FreeSWITCH-Hostname") != "h1.ip-switch.net" ||
ev.GetDirection("FreeSWITCH-Hostname") != "*out" ||
ev.GetTenant("FreeSWITCH-Hostname") != "h1.ip-switch.net" ||
ev.GetCategory("FreeSWITCH-Hostname") != "h1.ip-switch.net" ||
ev.GetAccount("FreeSWITCH-Hostname") != "h1.ip-switch.net" ||
ev.GetSubject("FreeSWITCH-Hostname") != "h1.ip-switch.net" ||
ev.GetDestination("FreeSWITCH-Hostname") != "h1.ip-switch.net" ||
setupTime != time.Date(2012, 10, 5, 13, 41, 38, 0, time.UTC) ||
answerTime != time.Date(2012, 10, 5, 13, 41, 38, 0, time.UTC) ||
dur != time.Duration(65)*time.Second {
t.Error("Values out of static not matching",
ev.GetReqType("FreeSWITCH-Hostname") != "h1.ip-switch.net",
ev.GetDirection("FreeSWITCH-Hostname") != "*out",
ev.GetTenant("FreeSWITCH-Hostname") != "h1.ip-switch.net",
ev.GetCategory("FreeSWITCH-Hostname") != "h1.ip-switch.net",
ev.GetAccount("FreeSWITCH-Hostname") != "h1.ip-switch.net",
ev.GetSubject("FreeSWITCH-Hostname") != "h1.ip-switch.net",
ev.GetDestination("FreeSWITCH-Hostname") != "h1.ip-switch.net",
setupTime != time.Date(2012, 10, 5, 13, 41, 38, 0, time.UTC),
answerTime != time.Date(2012, 10, 5, 13, 41, 38, 0, time.UTC),
dur != time.Duration(65)*time.Second)
}
}
func TestDDazEmptyTime(t *testing.T) {
body := `Event-Name: RE_SCHEDULE
Core-UUID: 792e181c-b6e6-499c-82a1-52a778e7d82d
FreeSWITCH-Hostname: h1.ip-switch.net
FreeSWITCH-Switchname: h1.ip-switch.net
FreeSWITCH-IPv4: 88.198.12.156
Caller-Channel-Created-Time: 0
Caller-Channel-Answered-Time
Task-Runtime: 1349437318`
var nilTime time.Time
ev := new(FSEvent).New(body)
if setupTime, err := ev.GetSetupTime(""); err != nil {
t.Error("Error when parsing empty setupTime")
} else if setupTime != nilTime {
t.Error("Expecting nil time, got: ", setupTime)
}
if answerTime, err := ev.GetAnswerTime(""); err != nil {
t.Error("Error when parsing empty setupTime")
} else if answerTime != nilTime {
t.Error("Expecting nil time, got: ", answerTime)
}
}
func TestParseFsHangup(t *testing.T) {
cfg, _ = config.NewDefaultCGRConfig()
config.SetCgrConfig(cfg)
ev := new(FSEvent).New(hangupEv)
@@ -498,3 +499,63 @@ variable_rtp_audio_rtcp_octet_count: 0
dur != time.Duration(5)*time.Second)
}
}
func TestParseEventValue(t *testing.T) {
cfg, _ = config.NewDefaultCGRConfig()
config.SetCgrConfig(cfg)
ev := new(FSEvent).New(hangupEv)
if cgrid := ev.ParseEventValue(&utils.RSRField{Id: utils.CGRID}); cgrid != "8b1ca78a9bbaa42c811e60b974188197c425dbe7" {
t.Error("Unexpected cgrid parsed", cgrid)
}
if tor := ev.ParseEventValue(&utils.RSRField{Id: utils.TOR}); tor != utils.VOICE {
t.Error("Unexpected tor parsed", tor)
}
if accid := ev.ParseEventValue(&utils.RSRField{Id: utils.ACCID}); accid != "37e9b766-5256-4e4b-b1ed-3767b930fec8" {
t.Error("Unexpected result parsed", accid)
}
if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.CDRHOST}); parsed != "10.0.2.15" {
t.Error("Unexpected result parsed", parsed)
}
if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.CDRSOURCE}); parsed != "FS_EVENT" {
t.Error("Unexpected result parsed", parsed)
}
if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.REQTYPE}); parsed != utils.PSEUDOPREPAID {
t.Error("Unexpected result parsed", parsed)
}
if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.DIRECTION}); parsed != utils.OUT {
t.Error("Unexpected result parsed", parsed)
}
if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.TENANT}); parsed != "cgrates.org" {
t.Error("Unexpected result parsed", parsed)
}
if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.CATEGORY}); parsed != "call" {
t.Error("Unexpected result parsed", parsed)
}
if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.ACCOUNT}); parsed != "1003" {
t.Error("Unexpected result parsed", parsed)
}
if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.SUBJECT}); parsed != "1003" {
t.Error("Unexpected result parsed", parsed)
}
if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.DESTINATION}); parsed != "1002" {
t.Error("Unexpected result parsed", parsed)
}
if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.SETUP_TIME}); parsed != "2014-04-25 18:08:27 +0200 CEST" {
t.Error("Unexpected result parsed", parsed)
}
if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.ANSWER_TIME}); parsed != "2014-04-25 18:08:40 +0200 CEST" {
t.Error("Unexpected result parsed", parsed)
}
if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.USAGE}); parsed != "5000000000" {
t.Error("Unexpected result parsed", parsed)
}
if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.MEDI_RUNID}); parsed != utils.DEFAULT_RUNID {
t.Error("Unexpected result parsed", parsed)
}
if parsed := ev.ParseEventValue(&utils.RSRField{Id: utils.COST}); parsed != "-1" {
t.Error("Unexpected result parsed", parsed)
}
if parsed := ev.ParseEventValue(&utils.RSRField{Id: "Hangup-Cause"}); parsed != "NORMAL_CLEARING" {
t.Error("Unexpected result parsed", parsed)
}
}

View File

@@ -161,6 +161,10 @@ func (sm *FSSessionManager) OnChannelPark(ev Event) {
}
dcs, _ = dcs.AppendDefaultRun()
for _, dc := range dcs {
dcRunFilter, _ := utils.NewRSRField(dc.RunFilter)
if dcRunFilter != nil && ev.ParseEventValue(&utils.RSRField{Id: dcRunFilter.Id}) != ev.ParseEventValue(dcRunFilter) {
engine.Logger.Info(fmt.Sprintf("Ignoring DerivedCharger with id %s due to non matching filter", dc.RunId))
}
startTime, err := ev.GetAnswerTime(PARK_TIME)
if err != nil {
engine.Logger.Err("Error parsing answer event start time, using time.Now!")

View File

@@ -24,67 +24,75 @@ import (
)
// Wraps regexp compiling in case of rsr fields
func NewDerivedCharger(runId, reqTypeFld, dirFld, tenantFld, catFld, acntFld, subjFld, dstFld, sTimeFld, aTimeFld, durFld string) (dc *DerivedCharger, err error) {
func NewDerivedCharger(runId, runFilter, reqTypeFld, dirFld, tenantFld, catFld, acntFld, subjFld, dstFld, sTimeFld, aTimeFld, durFld string) (dc *DerivedCharger, err error) {
if len(runId) == 0 {
return nil, errors.New("Empty run id field")
}
dc = &DerivedCharger{RunId: runId}
dc.RunFilter = runFilter
if strings.HasPrefix(dc.RunFilter, REGEXP_PREFIX) || strings.HasPrefix(dc.RunFilter, STATIC_VALUE_PREFIX) {
if dc.rsrRunFilter, err = NewRSRField(dc.RunFilter); err != nil {
return nil, err
} else if len(dc.rsrRunFilter.Id) == 0 {
return nil, errors.New("Empty filter header.")
}
}
dc.ReqTypeField = reqTypeFld
if strings.HasPrefix(dc.ReqTypeField, REGEXP_PREFIX) {
if strings.HasPrefix(dc.ReqTypeField, REGEXP_PREFIX) || strings.HasPrefix(dc.RunFilter, STATIC_VALUE_PREFIX) {
if dc.rsrReqTypeField, err = NewRSRField(dc.ReqTypeField); err != nil {
return nil, err
}
}
dc.DirectionField = dirFld
if strings.HasPrefix(dc.DirectionField, REGEXP_PREFIX) {
if strings.HasPrefix(dc.DirectionField, REGEXP_PREFIX) || strings.HasPrefix(dc.RunFilter, STATIC_VALUE_PREFIX) {
if dc.rsrDirectionField, err = NewRSRField(dc.DirectionField); err != nil {
return nil, err
}
}
dc.TenantField = tenantFld
if strings.HasPrefix(dc.TenantField, REGEXP_PREFIX) {
if strings.HasPrefix(dc.TenantField, REGEXP_PREFIX) || strings.HasPrefix(dc.RunFilter, STATIC_VALUE_PREFIX) {
if dc.rsrTenantField, err = NewRSRField(dc.TenantField); err != nil {
return nil, err
}
}
dc.CategoryField = catFld
if strings.HasPrefix(dc.CategoryField, REGEXP_PREFIX) {
if strings.HasPrefix(dc.CategoryField, REGEXP_PREFIX) || strings.HasPrefix(dc.RunFilter, STATIC_VALUE_PREFIX) {
if dc.rsrCategoryField, err = NewRSRField(dc.CategoryField); err != nil {
return nil, err
}
}
dc.AccountField = acntFld
if strings.HasPrefix(dc.AccountField, REGEXP_PREFIX) {
if strings.HasPrefix(dc.AccountField, REGEXP_PREFIX) || strings.HasPrefix(dc.RunFilter, STATIC_VALUE_PREFIX) {
if dc.rsrAccountField, err = NewRSRField(dc.AccountField); err != nil {
return nil, err
}
}
dc.SubjectField = subjFld
if strings.HasPrefix(dc.SubjectField, REGEXP_PREFIX) {
if strings.HasPrefix(dc.SubjectField, REGEXP_PREFIX) || strings.HasPrefix(dc.RunFilter, STATIC_VALUE_PREFIX) {
if dc.rsrSubjectField, err = NewRSRField(dc.SubjectField); err != nil {
return nil, err
}
}
dc.DestinationField = dstFld
if strings.HasPrefix(dc.DestinationField, REGEXP_PREFIX) {
if strings.HasPrefix(dc.DestinationField, REGEXP_PREFIX) || strings.HasPrefix(dc.RunFilter, STATIC_VALUE_PREFIX) {
if dc.rsrDestinationField, err = NewRSRField(dc.DestinationField); err != nil {
return nil, err
}
}
dc.SetupTimeField = sTimeFld
if strings.HasPrefix(dc.SetupTimeField, REGEXP_PREFIX) {
if strings.HasPrefix(dc.SetupTimeField, REGEXP_PREFIX) || strings.HasPrefix(dc.RunFilter, STATIC_VALUE_PREFIX) {
if dc.rsrSetupTimeField, err = NewRSRField(dc.SetupTimeField); err != nil {
return nil, err
}
}
dc.AnswerTimeField = aTimeFld
if strings.HasPrefix(dc.AnswerTimeField, REGEXP_PREFIX) {
if strings.HasPrefix(dc.AnswerTimeField, REGEXP_PREFIX) || strings.HasPrefix(dc.RunFilter, STATIC_VALUE_PREFIX) {
if dc.rsrAnswerTimeField, err = NewRSRField(dc.AnswerTimeField); err != nil {
return nil, err
}
}
dc.UsageField = durFld
if strings.HasPrefix(dc.UsageField, REGEXP_PREFIX) {
if strings.HasPrefix(dc.UsageField, REGEXP_PREFIX) || strings.HasPrefix(dc.RunFilter, STATIC_VALUE_PREFIX) {
if dc.rsrUsageField, err = NewRSRField(dc.UsageField); err != nil {
return nil, err
}
@@ -94,6 +102,7 @@ func NewDerivedCharger(runId, reqTypeFld, dirFld, tenantFld, catFld, acntFld, su
type DerivedCharger struct {
RunId string // Unique runId in the chain
RunFilter string // Only run the charger if the filter matches
ReqTypeField string // Field containing request type info, number in case of csv source, '^' as prefix in case of static values
DirectionField string // Field containing direction info
TenantField string // Field containing tenant info
@@ -104,7 +113,8 @@ type DerivedCharger struct {
SetupTimeField string // Field containing setup time information
AnswerTimeField string // Field containing answer time information
UsageField string // Field containing usage information
rsrReqTypeField *RSRField // Storage for compiled Regexp in case of RSRFields
rsrRunFilter *RSRField // Storage for compiled Regexp in case of RSRFields
rsrReqTypeField *RSRField
rsrDirectionField *RSRField
rsrTenantField *RSRField
rsrCategoryField *RSRField
@@ -136,7 +146,7 @@ func (dcs DerivedChargers) Append(dc *DerivedCharger) (DerivedChargers, error) {
}
func (dcs DerivedChargers) AppendDefaultRun() (DerivedChargers, error) {
dcDf, _ := NewDerivedCharger(DEFAULT_RUNID, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT,
dcDf, _ := NewDerivedCharger(DEFAULT_RUNID, "", META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT,
META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT)
return append(dcs, dcDf), nil
}

View File

@@ -47,6 +47,7 @@ func TestAppendDerivedChargers(t *testing.T) {
func TestNewDerivedCharger(t *testing.T) {
edc1 := &DerivedCharger{
RunId: "test1",
RunFilter: "",
ReqTypeField: "reqtype1",
DirectionField: "direction1",
TenantField: "tenant1",
@@ -58,7 +59,7 @@ func TestNewDerivedCharger(t *testing.T) {
AnswerTimeField: "answertime1",
UsageField: "duration1",
}
if dc1, err := NewDerivedCharger("test1", "reqtype1", "direction1", "tenant1", "tor1", "account1", "subject1", "destination1",
if dc1, err := NewDerivedCharger("test1", "", "reqtype1", "direction1", "tenant1", "tor1", "account1", "subject1", "destination1",
"setuptime1", "answertime1", "duration1"); err != nil {
t.Error("Unexpected error", err.Error)
} else if !reflect.DeepEqual(edc1, dc1) {
@@ -66,6 +67,7 @@ func TestNewDerivedCharger(t *testing.T) {
}
edc2 := &DerivedCharger{
RunId: "test2",
RunFilter: "^cdr_source/tdm_cdrs",
ReqTypeField: "~reqtype2:s/sip:(.+)/$1/",
DirectionField: "~direction2:s/sip:(.+)/$1/",
TenantField: "~tenant2:s/sip:(.+)/$1/",
@@ -77,6 +79,7 @@ func TestNewDerivedCharger(t *testing.T) {
AnswerTimeField: "~answertime2:s/sip:(.+)/$1/",
UsageField: "~duration2:s/sip:(.+)/$1/",
}
edc2.rsrRunFilter, _ = NewRSRField("^cdr_source/tdm_cdrs")
edc2.rsrReqTypeField, _ = NewRSRField("~reqtype2:s/sip:(.+)/$1/")
edc2.rsrDirectionField, _ = NewRSRField("~direction2:s/sip:(.+)/$1/")
edc2.rsrTenantField, _ = NewRSRField("~tenant2:s/sip:(.+)/$1/")
@@ -88,6 +91,7 @@ func TestNewDerivedCharger(t *testing.T) {
edc2.rsrAnswerTimeField, _ = NewRSRField("~answertime2:s/sip:(.+)/$1/")
edc2.rsrUsageField, _ = NewRSRField("~duration2:s/sip:(.+)/$1/")
if dc2, err := NewDerivedCharger("test2",
"^cdr_source/tdm_cdrs",
"~reqtype2:s/sip:(.+)/$1/",
"~direction2:s/sip:(.+)/$1/",
"~tenant2:s/sip:(.+)/$1/",
@@ -112,7 +116,7 @@ func TestDerivedChargersKey(t *testing.T) {
func TestAppendDefaultRun(t *testing.T) {
var dc1 DerivedChargers
dcDf := &DerivedCharger{RunId: DEFAULT_RUNID, ReqTypeField: META_DEFAULT, DirectionField: META_DEFAULT,
dcDf := &DerivedCharger{RunId: DEFAULT_RUNID, RunFilter: "", ReqTypeField: META_DEFAULT, DirectionField: META_DEFAULT,
TenantField: META_DEFAULT, CategoryField: META_DEFAULT, AccountField: META_DEFAULT, SubjectField: META_DEFAULT,
DestinationField: META_DEFAULT, SetupTimeField: META_DEFAULT, AnswerTimeField: META_DEFAULT, UsageField: META_DEFAULT}
eDc1 := DerivedChargers{dcDf}
@@ -120,7 +124,7 @@ func TestAppendDefaultRun(t *testing.T) {
t.Error("Unexpected result.")
}
dc2 := DerivedChargers{
&DerivedCharger{RunId: "extra1", ReqTypeField: "^prepaid", DirectionField: "*default", TenantField: "*default", CategoryField: "*default",
&DerivedCharger{RunId: "extra1", RunFilter: "", ReqTypeField: "reqtype2", DirectionField: "*default", TenantField: "*default", CategoryField: "*default",
AccountField: "rif", SubjectField: "rif", DestinationField: "*default", SetupTimeField: "*default", AnswerTimeField: "*default", UsageField: "*default"},
&DerivedCharger{RunId: "extra2", ReqTypeField: "*default", DirectionField: "*default", TenantField: "*default", CategoryField: "*default",
AccountField: "ivo", SubjectField: "ivo", DestinationField: "*default", SetupTimeField: "*default", AnswerTimeField: "*default", UsageField: "*default"},