This commit is contained in:
DanB
2018-05-16 14:21:27 +02:00
33 changed files with 2147 additions and 3661 deletions

View File

@@ -21,6 +21,7 @@ package agents
import (
"strings"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/sessions"
"github.com/cgrates/cgrates/utils"
)
@@ -174,7 +175,37 @@ func (smaEv *SMAsteriskEvent) ExtraParameters() (extraParams map[string]string)
return
}
func (smaEv *SMAsteriskEvent) AsSMGenericEvent() *sessions.SMGenericEvent {
func (smaEv *SMAsteriskEvent) UpdateCGREvent(cgrEv *utils.CGREvent) error {
resCGREv := *cgrEv
switch smaEv.EventType() {
case ARIChannelStateChange:
resCGREv.Event[utils.EVENT_NAME] = SMASessionStart
resCGREv.Event[utils.AnswerTime] = smaEv.Timestamp()
case ARIChannelDestroyed:
resCGREv.Event[utils.EVENT_NAME] = SMASessionTerminate
resCGREv.Event[utils.DISCONNECT_CAUSE] = smaEv.DisconnectCause()
if _, hasIt := resCGREv.Event[utils.AnswerTime]; !hasIt {
resCGREv.Event[utils.Usage] = "0s"
} else {
if aTime, err := utils.IfaceAsTime(resCGREv.Event[utils.AnswerTime], config.CgrConfig().DefaultTimezone); err != nil {
return err
} else if aTime.IsZero() {
resCGREv.Event[utils.Usage] = "0s"
} else {
actualTime, err := utils.ParseTimeDetectLayout(smaEv.Timestamp(), "")
if err != nil {
return err
}
resCGREv.Event[utils.Usage] = actualTime.Sub(aTime).String()
}
}
}
*cgrEv = resCGREv
return nil
}
func (smaEv *SMAsteriskEvent) AsMapStringInterface() (mp map[string]interface{}) {
mp = make(map[string]interface{})
var evName string
switch smaEv.EventType() {
case ARIStasisStart:
@@ -184,62 +215,144 @@ func (smaEv *SMAsteriskEvent) AsSMGenericEvent() *sessions.SMGenericEvent {
case ARIChannelDestroyed:
evName = SMASessionTerminate
}
smgEv := sessions.SMGenericEvent{utils.EVENT_NAME: evName}
smgEv[utils.OriginID] = smaEv.ChannelID()
mp[utils.EVENT_NAME] = evName
mp[utils.OriginID] = smaEv.ChannelID()
if smaEv.RequestType() != "" {
smgEv[utils.RequestType] = smaEv.RequestType()
mp[utils.RequestType] = smaEv.RequestType()
}
if smaEv.Tenant() != "" {
smgEv[utils.Tenant] = smaEv.Tenant()
mp[utils.Tenant] = smaEv.Tenant()
}
if smaEv.Category() != "" {
smgEv[utils.Category] = smaEv.Category()
mp[utils.Category] = smaEv.Category()
}
if smaEv.Subject() != "" {
smgEv[utils.Subject] = smaEv.Subject()
mp[utils.Subject] = smaEv.Subject()
}
smgEv[utils.OriginHost] = smaEv.OriginatorIP()
smgEv[utils.Account] = smaEv.Account()
smgEv[utils.Destination] = smaEv.Destination()
smgEv[utils.SetupTime] = smaEv.Timestamp()
mp[utils.OriginHost] = smaEv.OriginatorIP()
mp[utils.Account] = smaEv.Account()
mp[utils.Destination] = smaEv.Destination()
mp[utils.SetupTime] = smaEv.SetupTime()
if smaEv.Supplier() != "" {
smgEv[utils.SUPPLIER] = smaEv.Supplier()
mp[utils.SUPPLIER] = smaEv.Supplier()
}
for extraKey, extraVal := range smaEv.ExtraParameters() { // Append extraParameters
smgEv[extraKey] = extraVal
mp[extraKey] = extraVal
}
return &smgEv
return
}
// Updates fields in smgEv based on own fields
// Using pointer so we update it directly in cache
func (smaEv *SMAsteriskEvent) UpdateSMGEvent(smgEv *sessions.SMGenericEvent) error {
resSMGEv := *smgEv
switch smaEv.EventType() {
case ARIChannelStateChange:
if smaEv.ChannelState() == channelUp {
resSMGEv[utils.EVENT_NAME] = SMASessionStart
resSMGEv[utils.AnswerTime] = smaEv.Timestamp()
// AsCDR converts KamEvent into CGREvent
func (smaEv *SMAsteriskEvent) AsCGREvent(timezone string) (cgrEv *utils.CGREvent, err error) {
setupTime, err := utils.ParseTimeDetectLayout(
smaEv.Timestamp(), timezone)
if err != nil {
return
}
cgrEv = &utils.CGREvent{
Tenant: utils.FirstNonEmpty(smaEv.Tenant(),
config.CgrConfig().DefaultTenant),
ID: utils.UUIDSha1Prefix(),
Time: &setupTime,
Event: smaEv.AsMapStringInterface(),
}
return cgrEv, nil
}
func (smaEv *SMAsteriskEvent) V1AuthorizeArgs() (args *sessions.V1AuthorizeArgs) {
cgrEv, err := smaEv.AsCGREvent(config.CgrConfig().DefaultTimezone)
if err != nil {
return
}
args = &sessions.V1AuthorizeArgs{
GetMaxUsage: true,
CGREvent: *cgrEv,
}
// For the moment hardcoded only GetMaxUsage : true
/*
subsystems, has := kev[KamCGRSubsystems]
if !has {
return
}
case ARIChannelDestroyed:
resSMGEv[utils.EVENT_NAME] = SMASessionTerminate
resSMGEv[utils.DISCONNECT_CAUSE] = smaEv.DisconnectCause()
if _, hasIt := resSMGEv[utils.AnswerTime]; !hasIt {
resSMGEv[utils.Usage] = "0s"
} else {
if aTime, err := smgEv.GetAnswerTime(utils.META_DEFAULT, ""); err != nil {
return err
} else if aTime.IsZero() {
resSMGEv[utils.Usage] = "0s"
} else {
actualTime, err := utils.ParseTimeDetectLayout(smaEv.Timestamp(), "")
if err != nil {
return err
}
resSMGEv[utils.Usage] = actualTime.Sub(aTime).String()
if strings.Index(subsystems, utils.MetaAccounts) == -1 {
args.GetMaxUsage = false
}
if strings.Index(subsystems, utils.MetaResources) != -1 {
args.AuthorizeResources = true
}
if strings.Index(subsystems, utils.MetaSuppliers) != -1 {
args.GetSuppliers = true
if strings.Index(subsystems, utils.MetaSuppliersEventCost) != -1 {
args.SuppliersMaxCost = utils.MetaEventCost
}
if strings.Index(subsystems, utils.MetaSuppliersIgnoreErrors) != -1 {
args.SuppliersIgnoreErrors = true
}
}
}
*smgEv = resSMGEv
return nil
if strings.Index(subsystems, utils.MetaAttributes) != -1 {
args.GetAttributes = true
}
if strings.Index(subsystems, utils.MetaThresholds) != -1 {
args.ProcessThresholds = utils.BoolPointer(true)
}
if strings.Index(subsystems, utils.MetaStats) != -1 {
args.ProcessStatQueues = utils.BoolPointer(true)
}
*/
return
}
func (smaEv *SMAsteriskEvent) V1InitSessionArgs(cgrEv utils.CGREvent) (args *sessions.V1InitSessionArgs) {
args = &sessions.V1InitSessionArgs{ // defaults
InitSession: true,
CGREvent: cgrEv,
}
/*
subsystems, has := kev[KamCGRSubsystems]
if !has {
return
}
if strings.Index(subsystems, utils.MetaAccounts) == -1 {
args.InitSession = false
}
if strings.Index(subsystems, utils.MetaResources) != -1 {
args.AllocateResources = true
}
if strings.Index(subsystems, utils.MetaAttributes) != -1 {
args.GetAttributes = true
}
if strings.Index(subsystems, utils.MetaThresholds) != -1 {
args.ProcessThresholds = utils.BoolPointer(true)
}
if strings.Index(subsystems, utils.MetaStats) != -1 {
args.ProcessStatQueues = utils.BoolPointer(true)
}
*/
return
}
func (smaEv *SMAsteriskEvent) V1TerminateSessionArgs(cgrEv utils.CGREvent) (args *sessions.V1TerminateSessionArgs) {
args = &sessions.V1TerminateSessionArgs{ // defaults
TerminateSession: true,
CGREvent: cgrEv,
}
/*
subsystems, has := kev[KamCGRSubsystems]
if !has {
return
}
if strings.Index(subsystems, utils.MetaAccounts) == -1 {
args.TerminateSession = false
}
if strings.Index(subsystems, utils.MetaResources) != -1 {
args.ReleaseResources = true
}
if strings.Index(subsystems, utils.MetaThresholds) != -1 {
args.ProcessThresholds = utils.BoolPointer(true)
}
if strings.Index(subsystems, utils.MetaStats) != -1 {
args.ProcessStatQueues = utils.BoolPointer(true)
}
*/
return
}

View File

@@ -21,9 +21,6 @@ import (
"encoding/json"
"reflect"
"testing"
"github.com/cgrates/cgrates/sessions"
"github.com/cgrates/cgrates/utils"
)
var (
@@ -328,212 +325,3 @@ func TestSMAEventExtraParameters(t *testing.T) {
t.Errorf("Expecting: %+v, received: %+v", expExtraParams, extraParams)
}
}
/*
func TestSMAEventUpdateFromEvent(t *testing.T) {
var ev map[string]interface{}
if err := json.Unmarshal([]byte(stasisStart), &ev); err != nil {
t.Error(err)
}
smaEv := NewSMAsteriskEvent(ev, "127.0.0.1")
ev = make(map[string]interface{})
if err := json.Unmarshal([]byte(channelAnsweredDestroyed), &ev); err != nil {
t.Error(err)
}
smaEv2 := NewSMAsteriskEvent(ev, "127.0.0.1")
smaEv.UpdateFromEvent(smaEv2)
eSMAEv := &SMAsteriskEvent{
ariEv: map[string]interface{}{
"type": "ChannelDestroyed",
"args": []interface{}{"cgr_reqtype=*prepaid", "cgr_destination=1003", "extra1=val1", "extra2=val2"},
"timestamp": "2016-09-12T13:54:27.335+0200",
"application": "cgrates_auth",
"cause_txt": "Normal Clearing",
"channel": map[string]interface{}{
"id": "1473681228.6",
"state": "Up",
"name": "PJSIP/1001-00000004",
"caller": map[string]interface{}{
"name": "1001",
"number": "1001"},
"language": "en",
"connected": map[string]interface{}{
"name": "",
"number": "1002"},
"accountcode": "",
"dialplan": map[string]interface{}{
"context": "internal",
"exten": "1002",
"priority": 3.0},
"creationtime": "2016-09-12T13:53:48.918+0200"},
"cause": 16.0},
asteriskIP: "127.0.0.1",
cachedFields: map[string]string{"cgr_reqtype": "*prepaid",
"cgr_destination": "1003", "extra1": "val1", "extra2": "val2"},
}
if !reflect.DeepEqual(eSMAEv, smaEv) {
t.Errorf("Expecting: %+v, received: %+v", eSMAEv, smaEv)
}
}
*/
//Here
func TestSMAEventAsSMGenericEvent(t *testing.T) {
var ev map[string]interface{}
if err := json.Unmarshal([]byte(stasisStart), &ev); err != nil {
t.Error(err)
}
eSMGEv := &sessions.SMGenericEvent{
utils.EVENT_NAME: SMAAuthorization,
utils.OriginID: "1473681228.6",
utils.RequestType: "*prepaid",
utils.OriginHost: "127.0.0.1",
utils.Account: "1001",
utils.Destination: "1002",
utils.SetupTime: "2016-09-12T13:53:48.919+0200",
utils.SUPPLIER: "supplier1",
"extra1": "val1",
"extra2": "val2",
}
smaEv := NewSMAsteriskEvent(ev, "127.0.0.1")
if smgEv := smaEv.AsSMGenericEvent(); !reflect.DeepEqual(eSMGEv, smgEv) {
t.Errorf("Expecting: %+v, received: %+v", eSMGEv, smgEv)
}
}
func TestSMAEventUpdateSMGEventAnswered(t *testing.T) {
var ev map[string]interface{}
if err := json.Unmarshal([]byte(channelStateChange), &ev); err != nil {
t.Error(err)
}
smaEv := NewSMAsteriskEvent(ev, "127.0.0.1")
smgEv := &sessions.SMGenericEvent{
utils.EVENT_NAME: SMAAuthorization,
utils.OriginID: "1473681228.6",
utils.RequestType: "*prepaid",
utils.OriginHost: "127.0.0.1",
utils.Account: "1001",
utils.Destination: "1003",
utils.SetupTime: "2016-09-12T13:53:48.919+0200",
"extra1": "val1",
"extra2": "val2",
}
eSMGEv := &sessions.SMGenericEvent{
utils.EVENT_NAME: SMASessionStart,
utils.OriginID: "1473681228.6",
utils.RequestType: "*prepaid",
utils.OriginHost: "127.0.0.1",
utils.Account: "1001",
utils.Destination: "1003",
utils.SetupTime: "2016-09-12T13:53:48.919+0200",
utils.AnswerTime: "2016-09-12T13:53:52.110+0200",
"extra1": "val1",
"extra2": "val2",
}
if err := smaEv.UpdateSMGEvent(smgEv); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eSMGEv, smgEv) {
t.Errorf("Expecting: %+v, received: %+v", eSMGEv, smgEv)
}
// Apply update using a terminate event
ev = make(map[string]interface{})
if err = json.Unmarshal([]byte(channelAnsweredDestroyed), &ev); err != nil {
t.Error(err)
}
smaEv = NewSMAsteriskEvent(ev, "127.0.0.1")
eSMGEv = &sessions.SMGenericEvent{
utils.EVENT_NAME: SMASessionTerminate,
utils.OriginID: "1473681228.6",
utils.RequestType: "*prepaid",
utils.OriginHost: "127.0.0.1",
utils.Account: "1001",
utils.Destination: "1003",
utils.SetupTime: "2016-09-12T13:53:48.919+0200",
utils.AnswerTime: "2016-09-12T13:53:52.110+0200",
utils.Usage: "35.225s",
utils.DISCONNECT_CAUSE: "Normal Clearing",
"extra1": "val1",
"extra2": "val2",
}
if err := smaEv.UpdateSMGEvent(smgEv); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eSMGEv, smgEv) {
t.Errorf("Expecting: %+v, received: %+v", eSMGEv, smgEv)
}
}
func TestSMAEventUpdateSMGEventUnaswered(t *testing.T) {
smgEv := &sessions.SMGenericEvent{
utils.EVENT_NAME: SMAAuthorization,
utils.OriginID: "1473681228.6",
utils.RequestType: "*prepaid",
utils.OriginHost: "127.0.0.1",
utils.Account: "1001",
utils.Destination: "1003",
utils.SetupTime: "2016-09-12T13:53:48.919+0200",
"extra1": "val1",
"extra2": "val2",
}
eSMGEv := &sessions.SMGenericEvent{
utils.EVENT_NAME: SMASessionTerminate,
utils.OriginID: "1473681228.6",
utils.RequestType: "*prepaid",
utils.OriginHost: "127.0.0.1",
utils.Account: "1001",
utils.Destination: "1003",
utils.SetupTime: "2016-09-12T13:53:48.919+0200",
utils.Usage: "0s",
utils.DISCONNECT_CAUSE: "Normal Clearing",
"extra1": "val1",
"extra2": "val2",
}
// Apply update using a terminate event
ev := make(map[string]interface{})
if err := json.Unmarshal([]byte(channelUnansweredDestroyed), &ev); err != nil {
t.Error(err)
}
smaEv := NewSMAsteriskEvent(ev, "127.0.0.1")
if err := smaEv.UpdateSMGEvent(smgEv); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eSMGEv, smgEv) {
t.Errorf("Expecting: %+v, received: %+v", eSMGEv, smgEv)
}
}
func TestSMAEventUpdateSMGEventBusy(t *testing.T) {
smgEv := &sessions.SMGenericEvent{
utils.EVENT_NAME: SMAAuthorization,
utils.OriginID: "1473681228.6",
utils.RequestType: "*prepaid",
utils.OriginHost: "127.0.0.1",
utils.Account: "1001",
utils.Destination: "1003",
utils.SetupTime: "2016-09-12T13:53:48.919+0200",
"extra1": "val1",
"extra2": "val2",
}
eSMGEv := &sessions.SMGenericEvent{
utils.EVENT_NAME: SMASessionTerminate,
utils.OriginID: "1473681228.6",
utils.RequestType: "*prepaid",
utils.OriginHost: "127.0.0.1",
utils.Account: "1001",
utils.Destination: "1003",
utils.SetupTime: "2016-09-12T13:53:48.919+0200",
utils.Usage: "0s",
utils.DISCONNECT_CAUSE: "User busy",
"extra1": "val1",
"extra2": "val2",
}
// Apply update using a terminate event
ev := make(map[string]interface{})
if err := json.Unmarshal([]byte(channelBusyDestroyed), &ev); err != nil {
t.Error(err)
}
smaEv := NewSMAsteriskEvent(ev, "127.0.0.1")
if err := smaEv.UpdateSMGEvent(smgEv); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eSMGEv, smgEv) {
t.Errorf("Expecting: %+v, received: %+v", eSMGEv, smgEv)
}
}

View File

@@ -48,30 +48,33 @@ const (
SMASessionTerminate = "SMA_SESSION_TERMINATE"
)
func NewSMAsterisk(cgrCfg *config.CGRConfig, astConnIdx int, smgConn *utils.BiRPCInternalClient) (*SMAsterisk, error) {
sma := &SMAsterisk{cgrCfg: cgrCfg, smg: smgConn,
eventsCache: make(map[string]*sessions.SMGenericEvent)}
func NewAsteriskAgent(cgrCfg *config.CGRConfig, astConnIdx int,
smgConn *utils.BiRPCInternalClient) (*AsteriskAgent, error) {
sma := &AsteriskAgent{cgrCfg: cgrCfg, smg: smgConn,
eventsCache: make(map[string]*utils.CGREvent)}
sma.smg.SetClientConn(sma) // pass the connection to SMA back into smg so we can receive the disconnects
return sma, nil
}
type SMAsterisk struct {
type AsteriskAgent struct {
cgrCfg *config.CGRConfig // Separate from smCfg since there can be multiple
astConnIdx int
smg *utils.BiRPCInternalClient
astConn *aringo.ARInGO
astEvChan chan map[string]interface{}
astErrChan chan error
eventsCache map[string]*sessions.SMGenericEvent // used to gather information about events during various phases
evCacheMux sync.RWMutex // Protect eventsCache
eventsCache map[string]*utils.CGREvent // used to gather information about events during various phases
evCacheMux sync.RWMutex // Protect eventsCache
}
func (sma *SMAsterisk) connectAsterisk() (err error) {
func (sma *AsteriskAgent) connectAsterisk() (err error) {
connCfg := sma.cgrCfg.AsteriskAgentCfg().AsteriskConns[sma.astConnIdx]
sma.astEvChan = make(chan map[string]interface{})
sma.astErrChan = make(chan error)
sma.astConn, err = aringo.NewARInGO(fmt.Sprintf("ws://%s/ari/events?api_key=%s:%s&app=%s", connCfg.Address, connCfg.User, connCfg.Password, CGRAuthAPP), "http://cgrates.org",
connCfg.User, connCfg.Password, fmt.Sprintf("%s %s", utils.CGRateS, utils.VERSION), sma.astEvChan, sma.astErrChan, connCfg.ConnectAttempts, connCfg.Reconnects)
sma.astConn, err = aringo.NewARInGO(fmt.Sprintf("ws://%s/ari/events?api_key=%s:%s&app=%s",
connCfg.Address, connCfg.User, connCfg.Password, CGRAuthAPP), "http://cgrates.org",
connCfg.User, connCfg.Password, fmt.Sprintf("%s %s", utils.CGRateS, utils.VERSION),
sma.astEvChan, sma.astErrChan, connCfg.ConnectAttempts, connCfg.Reconnects)
if err != nil {
return err
}
@@ -79,7 +82,7 @@ func (sma *SMAsterisk) connectAsterisk() (err error) {
}
// Called to start the service
func (sma *SMAsterisk) ListenAndServe() (err error) {
func (sma *AsteriskAgent) ListenAndServe() (err error) {
if err := sma.connectAsterisk(); err != nil {
return err
}
@@ -88,7 +91,8 @@ func (sma *SMAsterisk) ListenAndServe() (err error) {
case err = <-sma.astErrChan:
return
case astRawEv := <-sma.astEvChan:
smAsteriskEvent := NewSMAsteriskEvent(astRawEv, strings.Split(sma.cgrCfg.AsteriskAgentCfg().AsteriskConns[sma.astConnIdx].Address, ":")[0])
smAsteriskEvent := NewSMAsteriskEvent(astRawEv,
strings.Split(sma.cgrCfg.AsteriskAgentCfg().AsteriskConns[sma.astConnIdx].Address, ":")[0])
switch smAsteriskEvent.EventType() {
case ARIStasisStart:
go sma.handleStasisStart(smAsteriskEvent)
@@ -99,52 +103,69 @@ func (sma *SMAsterisk) ListenAndServe() (err error) {
}
}
}
panic("<SMAsterisk> ListenAndServe out of select")
panic("<AsteriskAgent> ListenAndServe out of select")
}
// hangupChannel will disconnect from CGRateS side with congestion reason
func (sma *SMAsterisk) hangupChannel(channelID string) (err error) {
func (sma *AsteriskAgent) hangupChannel(channelID string) (err error) {
_, err = sma.astConn.Call(aringo.HTTP_DELETE, fmt.Sprintf("http://%s/ari/channels/%s",
sma.cgrCfg.AsteriskAgentCfg().AsteriskConns[sma.astConnIdx].Address, channelID),
url.Values{"reason": {"congestion"}})
return
}
func (sma *SMAsterisk) handleStasisStart(ev *SMAsteriskEvent) {
func (sma *AsteriskAgent) handleStasisStart(ev *SMAsteriskEvent) {
// Subscribe for channel updates even after we leave Stasis
if _, err := sma.astConn.Call(aringo.HTTP_POST, fmt.Sprintf("http://%s/ari/applications/%s/subscription?eventSource=channel:%s",
sma.cgrCfg.AsteriskAgentCfg().AsteriskConns[sma.astConnIdx].Address, CGRAuthAPP, ev.ChannelID()), nil); err != nil {
utils.Logger.Err(fmt.Sprintf("<SMAsterisk> Error: %s when subscribing to events for channelID: %s", err.Error(), ev.ChannelID()))
if _, err := sma.astConn.Call(aringo.HTTP_POST,
fmt.Sprintf("http://%s/ari/applications/%s/subscription?eventSource=channel:%s",
sma.cgrCfg.AsteriskAgentCfg().AsteriskConns[sma.astConnIdx].Address,
CGRAuthAPP, ev.ChannelID()), nil); err != nil {
utils.Logger.Err(fmt.Sprintf("<%s> Error: %s when subscribingto events for channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
// Since we got error, disconnect channel
if err := sma.hangupChannel(ev.ChannelID()); err != nil {
utils.Logger.Err(fmt.Sprintf("<SMAsterisk> Error: %s when attempting to disconnect channelID: %s", err.Error(), ev.ChannelID()))
utils.Logger.Err(fmt.Sprintf("<%s> Error: %s when attempting to disconnect channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
}
return
}
// Query the SMG via RPC for maxUsage
var maxUsage float64
smgEv := ev.AsSMGenericEvent()
if err := sma.smg.Call("SMGenericV1.GetMaxUsage", *smgEv, &maxUsage); err != nil {
utils.Logger.Err(fmt.Sprintf("<SMAsterisk> Error: %s when attempting to authorize session for channelID: %s", err.Error(), ev.ChannelID()))
//authorize Session
authArgs := ev.V1AuthorizeArgs()
if authArgs == nil {
utils.Logger.Err(fmt.Sprintf("<%s> event: %s cannot generate auth session arguments",
utils.AsteriskAgent, ev.ChannelID()))
return
}
var authReply sessions.V1AuthorizeReply
if err := sma.smg.Call(utils.SessionSv1AuthorizeEvent, authArgs, &authReply); err != nil {
utils.Logger.Err(fmt.Sprintf("<%s> Error: %s when attempting to authorize session for channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
if err := sma.hangupChannel(ev.ChannelID()); err != nil {
utils.Logger.Err(fmt.Sprintf("<SMAsterisk> Error: %s when attempting to disconnect channelID: %s", err.Error(), ev.ChannelID()))
utils.Logger.Err(fmt.Sprintf("<%s> Error: %s when attempting to disconnect channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
}
return
}
if maxUsage == 0 {
if authReply.MaxUsage != nil && *authReply.MaxUsage == time.Duration(0) {
if err := sma.hangupChannel(ev.ChannelID()); err != nil {
utils.Logger.Err(fmt.Sprintf("<SMAsterisk> Error: %s when attempting to disconnect channelID: %s", err.Error(), ev.ChannelID()))
utils.Logger.Err(fmt.Sprintf("<%s> Error: %s when attempting to disconnect channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
}
return
} else if maxUsage != -1 {
} else if *authReply.MaxUsage != time.Duration(-1) {
// Set absolute timeout for non-postpaid calls
if _, err := sma.astConn.Call(aringo.HTTP_POST, fmt.Sprintf("http://%s/ari/channels/%s/variable?variable=%s", // Asterisk having issue with variable terminating empty so harcoding param in url
sma.cgrCfg.AsteriskAgentCfg().AsteriskConns[sma.astConnIdx].Address, ev.ChannelID(), CGRMaxSessionTime),
url.Values{"value": {strconv.FormatFloat(maxUsage*1000, 'f', -1, 64)}}); err != nil { // Asterisk expects value in ms
utils.Logger.Err(fmt.Sprintf("<SMAsterisk> Error: %s when setting %s for channelID: %s", err.Error(), CGRMaxSessionTime, ev.ChannelID()))
if _, err := sma.astConn.Call(aringo.HTTP_POST,
fmt.Sprintf("http://%s/ari/channels/%s/variable?variable=%s&value=%d", // Asterisk having issue with variable terminating empty so harcoding param in url
sma.cgrCfg.AsteriskAgentCfg().AsteriskConns[sma.astConnIdx].Address,
ev.ChannelID(), CGRMaxSessionTime, int64(authReply.MaxUsage.Seconds()*1000)),
url.Values{"value": {strconv.FormatFloat(
authReply.MaxUsage.Seconds()*1000, 'f', -1, 64)}}); err != nil { // Asterisk expects value in ms
utils.Logger.Err(fmt.Sprintf("<%s> Error: %s when setting %s for channelID: %s",
utils.AsteriskAgent, err.Error(), CGRMaxSessionTime, ev.ChannelID()))
// Since we got error, disconnect channel
if err := sma.hangupChannel(ev.ChannelID()); err != nil {
utils.Logger.Err(fmt.Sprintf("<SMAsterisk> Error: %s when attempting to disconnect channelID: %s", err.Error(), ev.ChannelID()))
utils.Logger.Err(fmt.Sprintf("<%s> Error: %s when attempting to disconnect channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
}
return
}
@@ -152,108 +173,133 @@ func (sma *SMAsterisk) handleStasisStart(ev *SMAsteriskEvent) {
// Exit channel from stasis
if _, err := sma.astConn.Call(aringo.HTTP_POST, fmt.Sprintf("http://%s/ari/channels/%s/continue",
sma.cgrCfg.AsteriskAgentCfg().AsteriskConns[sma.astConnIdx].Address, ev.ChannelID()), nil); err != nil {
sma.cgrCfg.AsteriskAgentCfg().AsteriskConns[sma.astConnIdx].Address,
ev.ChannelID()), nil); err != nil {
}
// Done with processing event, cache it for later use
sma.evCacheMux.Lock()
sma.eventsCache[ev.ChannelID()] = smgEv
sma.eventsCache[ev.ChannelID()] = &authArgs.CGREvent
sma.evCacheMux.Unlock()
}
// Ussually channelUP
func (sma *SMAsterisk) handleChannelStateChange(ev *SMAsteriskEvent) {
func (sma *AsteriskAgent) handleChannelStateChange(ev *SMAsteriskEvent) {
if ev.ChannelState() != channelUp {
return
}
sma.evCacheMux.RLock()
smgEv, hasIt := sma.eventsCache[ev.ChannelID()]
cgrEv, hasIt := sma.eventsCache[ev.ChannelID()]
sma.evCacheMux.RUnlock()
if !hasIt { // Not handled by us
return
}
sma.evCacheMux.Lock()
err := ev.UpdateSMGEvent(smgEv) // Updates the event directly in the cache
err := ev.UpdateCGREvent(cgrEv) // Updates the event directly in the cache
sma.evCacheMux.Unlock()
if err != nil {
utils.Logger.Err(
fmt.Sprintf("<SMAsterisk> Error: %s when attempting to initiate session for channelID: %s",
err.Error(), ev.ChannelID()))
fmt.Sprintf("<%s> Error: %s when attempting to initiate session for channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
if err := sma.hangupChannel(ev.ChannelID()); err != nil {
utils.Logger.Err(
fmt.Sprintf("<SMAsterisk> Error: %s when attempting to disconnect channelID: %s",
err.Error(), ev.ChannelID()))
fmt.Sprintf("<%s> Error: %s when attempting to disconnect channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
}
return
}
var maxUsage time.Duration
if err := sma.smg.Call(utils.SMGenericV2InitiateSession,
*smgEv, &maxUsage); err != nil {
// populate init session args
initSessionArgs := ev.V1InitSessionArgs(*cgrEv)
if initSessionArgs == nil {
utils.Logger.Err(fmt.Sprintf("<%s> event: %s cannot generate init session arguments",
utils.AsteriskAgent, ev.ChannelID()))
return
}
//initit Session
var initReply sessions.V1InitSessionReply
if err := sma.smg.Call(utils.SessionSv1InitiateSession,
initSessionArgs, &initReply); err != nil {
utils.Logger.Err(
fmt.Sprintf("<SMAsterisk> Error: %s when attempting to initiate session for channelID: %s",
err.Error(), ev.ChannelID()))
fmt.Sprintf("<%s> Error: %s when attempting to initiate session for channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
if err := sma.hangupChannel(ev.ChannelID()); err != nil {
utils.Logger.Err(
fmt.Sprintf("<SMAsterisk> Error: %s when attempting to disconnect channelID: %s",
err.Error(), ev.ChannelID()))
fmt.Sprintf("<%s> Error: %s when attempting to disconnect channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
}
return
} else if maxUsage == 0 {
} else if initReply.MaxUsage != nil && *initReply.MaxUsage == time.Duration(0) {
if err := sma.hangupChannel(ev.ChannelID()); err != nil {
utils.Logger.Err(
fmt.Sprintf("<SMAsterisk> Error: %s when attempting to disconnect channelID: %s",
err.Error(), ev.ChannelID()))
fmt.Sprintf("<%s> Error: %s when attempting to disconnect channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
}
return
}
}
// Channel disconnect
func (sma *SMAsterisk) handleChannelDestroyed(ev *SMAsteriskEvent) {
func (sma *AsteriskAgent) handleChannelDestroyed(ev *SMAsteriskEvent) {
sma.evCacheMux.RLock()
smgEv, hasIt := sma.eventsCache[ev.ChannelID()]
cgrEv, hasIt := sma.eventsCache[ev.ChannelID()]
sma.evCacheMux.RUnlock()
if !hasIt { // Not handled by us
return
}
sma.evCacheMux.Lock()
err := ev.UpdateSMGEvent(smgEv) // Updates the event directly in the cache
err := ev.UpdateCGREvent(cgrEv) // Updates the event directly in the cache
sma.evCacheMux.Unlock()
if err != nil {
utils.Logger.Err(fmt.Sprintf("<SMAsterisk> Error: %s when attempting to initiate session for channelID: %s", err.Error(), ev.ChannelID()))
utils.Logger.Err(fmt.Sprintf("<%s> Error: %s when attempting to initiate session for channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
if err := sma.hangupChannel(ev.ChannelID()); err != nil {
utils.Logger.Err(fmt.Sprintf("<SMAsterisk> Error: %s when attempting to disconnect channelID: %s", err.Error(), ev.ChannelID()))
utils.Logger.Err(fmt.Sprintf("<%s> Error: %s when attempting to disconnect channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
}
return
}
// populate terminate session args
tsArgs := ev.V1TerminateSessionArgs(*cgrEv)
if tsArgs == nil {
utils.Logger.Err(fmt.Sprintf("<%s> event: %s cannot generate terminate session arguments",
utils.AsteriskAgent, ev.ChannelID()))
return
}
var reply string
if err := sma.smg.Call("SMGenericV1.TerminateSession", *smgEv, &reply); err != nil {
utils.Logger.Err(fmt.Sprintf("<SMAsterisk> Error: %s when attempting to terminate session for channelID: %s", err.Error(), ev.ChannelID()))
if err := sma.smg.Call(utils.SessionSv1TerminateSession,
tsArgs, &reply); err != nil {
utils.Logger.Err(fmt.Sprintf("<%s> Error: %s when attempting to terminate session for channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
}
if sma.cgrCfg.AsteriskAgentCfg().CreateCDR {
if err := sma.smg.Call("SMGenericV1.ProcessCDR", *smgEv, &reply); err != nil {
utils.Logger.Err(fmt.Sprintf("<SMAsterisk> Error: %s when attempting to process CDR for channelID: %s", err.Error(), ev.ChannelID()))
if err := sma.smg.Call(utils.SessionSv1ProcessCDR, *cgrEv, &reply); err != nil {
utils.Logger.Err(fmt.Sprintf("<%s> Error: %s when attempting to process CDR for channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
}
}
}
// Called to shutdown the service
func (sma *SMAsterisk) ServiceShutdown() error {
func (sma *AsteriskAgent) ServiceShutdown() error {
return nil
}
// Internal method to disconnect session in asterisk
func (sma *SMAsterisk) V1DisconnectSession(args utils.AttrDisconnectSession, reply *string) error {
func (sma *AsteriskAgent) V1DisconnectSession(args utils.AttrDisconnectSession, reply *string) error {
channelID := sessions.SMGenericEvent(args.EventStart).GetOriginID(utils.META_DEFAULT)
if err := sma.hangupChannel(channelID); err != nil {
utils.Logger.Err(
fmt.Sprintf("<SMAsterisk> Error: %s when attempting to disconnect channelID: %s",
err.Error(), channelID))
fmt.Sprintf("<%s> Error: %s when attempting to disconnect channelID: %s",
utils.AsteriskAgent, err.Error(), channelID))
}
*reply = utils.OK
return nil
}
// rpcclient.RpcClientConnection interface
func (sma *SMAsterisk) Call(serviceMethod string, args interface{}, reply interface{}) error {
func (sma *AsteriskAgent) Call(serviceMethod string, args interface{}, reply interface{}) error {
return utils.RPCCall(sma, serviceMethod, args, reply)
}

View File

@@ -241,7 +241,7 @@ func startAsteriskAgent(internalSMGChan chan rpcclient.RpcClientConnection, exit
internalSMGChan <- smgRpcConn
birpcClnt := utils.NewBiRPCInternalClient(smgRpcConn.(*sessions.SMGeneric))
for connIdx := range cfg.AsteriskAgentCfg().AsteriskConns { // Instantiate connections towards asterisk servers
sma, err := agents.NewSMAsterisk(cfg, connIdx, birpcClnt)
sma, err := agents.NewAsteriskAgent(cfg, connIdx, birpcClnt)
if err != nil {
utils.Logger.Err(fmt.Sprintf("<SMAsterisk> error: %s!", err))
exitChan <- true

View File

@@ -0,0 +1,69 @@
{
// CGRateS Configuration file
"general": {
"log_level": 7,
"reply_timeout": "30s",
"dbdata_encoding": "json",
},
"listen": {
"rpc_json": ":2012",
"rpc_gob": ":2013",
"http": ":2080",
},
"data_db": {
"db_type": "mongo",
"db_name": "10",
"db_port": 27017,
},
"stor_db": {
"db_type": "mongo",
"db_port": 27017,
},
"cache":{
"destinations": {"limit": 10000, "ttl":"0s", "precache": true},
"reverse_destinations": {"limit": 10000, "ttl":"0s", "precache": true},
"rating_plans": {"limit": 10000, "ttl":"0s","precache": true},
"rating_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"lcr_rules": {"limit": 10000, "ttl":"0s", "precache": true},
"cdr_stats": {"limit": 10000, "ttl":"0s", "precache": true},
"actions": {"limit": 10000, "ttl":"0s", "precache": true},
"action_plans": {"limit": 10000, "ttl":"0s", "precache": true},
"account_action_plans": {"limit": 10000, "ttl":"0s", "precache": true},
"action_triggers": {"limit": 10000, "ttl":"0s", "precache": true},
"shared_groups": {"limit": 10000, "ttl":"0s", "precache": true},
"aliases": {"limit": 10000, "ttl":"0s", "precache": true},
"reverse_aliases": {"limit": 10000, "ttl":"0s", "precache": true},
"derived_chargers": {"limit": 10000, "ttl":"0s", "precache": true},
"resource_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"resources": {"limit": 10000, "ttl":"0s", "precache": true},
"statqueues": {"limit": 10000, "ttl":"0s", "precache": true},
"statqueue_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"thresholds": {"limit": 10000, "ttl":"0s", "precache": true},
"threshold_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"filters": {"limit": 10000, "ttl":"0s", "precache": true},
"supplier_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"attribute_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"resource_filter_indexes" :{"limit": 10000, "ttl":"0s"},
"resource_filter_revindexes" : {"limit": 10000, "ttl":"0s"},
"stat_filter_indexes" : {"limit": 10000, "ttl":"0s"},
"stat_filter_revindexes" : {"limit": 10000, "ttl":"0s"},
"threshold_filter_indexes" : {"limit": 10000, "ttl":"0s"},
"threshold_filter_revindexes" : {"limit": 10000, "ttl":"0s"},
"supplier_filter_indexes" : {"limit": 10000, "ttl":"0s"},
"supplier_filter_revindexes" :{"limit": 10000, "ttl":"0s"},
"attribute_filter_indexes" : {"limit": 10000, "ttl":"0s"},
"attribute_filter_revindexes" : {"limit": 10000, "ttl":"0s"},
},
}

View File

@@ -0,0 +1,69 @@
{
// CGRateS Configuration file
"general": {
"log_level": 7,
"reply_timeout": "30s",
"dbdata_encoding": "msgpack",
},
"listen": {
"rpc_json": ":2012",
"rpc_gob": ":2013",
"http": ":2080",
},
"data_db": {
"db_type": "mongo",
"db_name": "11",
"db_port": 27017,
},
"stor_db": {
"db_type": "mongo",
"db_port": 27017,
},
"cache":{
"destinations": {"limit": 10000, "ttl":"0s", "precache": true},
"reverse_destinations": {"limit": 10000, "ttl":"0s", "precache": true},
"rating_plans": {"limit": 10000, "ttl":"0s","precache": true},
"rating_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"lcr_rules": {"limit": 10000, "ttl":"0s", "precache": true},
"cdr_stats": {"limit": 10000, "ttl":"0s", "precache": true},
"actions": {"limit": 10000, "ttl":"0s", "precache": true},
"action_plans": {"limit": 10000, "ttl":"0s", "precache": true},
"account_action_plans": {"limit": 10000, "ttl":"0s", "precache": true},
"action_triggers": {"limit": 10000, "ttl":"0s", "precache": true},
"shared_groups": {"limit": 10000, "ttl":"0s", "precache": true},
"aliases": {"limit": 10000, "ttl":"0s", "precache": true},
"reverse_aliases": {"limit": 10000, "ttl":"0s", "precache": true},
"derived_chargers": {"limit": 10000, "ttl":"0s", "precache": true},
"resource_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"resources": {"limit": 10000, "ttl":"0s", "precache": true},
"statqueues": {"limit": 10000, "ttl":"0s", "precache": true},
"statqueue_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"thresholds": {"limit": 10000, "ttl":"0s", "precache": true},
"threshold_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"filters": {"limit": 10000, "ttl":"0s", "precache": true},
"supplier_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"attribute_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"resource_filter_indexes" :{"limit": 10000, "ttl":"0s"},
"resource_filter_revindexes" : {"limit": 10000, "ttl":"0s"},
"stat_filter_indexes" : {"limit": 10000, "ttl":"0s"},
"stat_filter_revindexes" : {"limit": 10000, "ttl":"0s"},
"threshold_filter_indexes" : {"limit": 10000, "ttl":"0s"},
"threshold_filter_revindexes" : {"limit": 10000, "ttl":"0s"},
"supplier_filter_indexes" : {"limit": 10000, "ttl":"0s"},
"supplier_filter_revindexes" :{"limit": 10000, "ttl":"0s"},
"attribute_filter_indexes" : {"limit": 10000, "ttl":"0s"},
"attribute_filter_revindexes" : {"limit": 10000, "ttl":"0s"},
},
}

View File

@@ -0,0 +1,67 @@
{
// CGRateS Configuration file
//
"general": {
"log_level": 7,
"dbdata_encoding": "json", // encoding used to store object data in strings: <msgpack|json>
},
"listen": {
"rpc_json": ":2012",
"rpc_gob": ":2013",
"http": ":2080",
},
"data_db": { // database used to store runtime data (eg: accounts, cdr stats)
"db_type": "redis", // data_db type: <redis|mongo>
"db_port": 6379, // data_db port to reach the database
"db_name": "10", // data_db database name to connect to
},
"stor_db": {
"db_password": "CGRateS.org",
},
"cache":{
"destinations": {"limit": 10000, "ttl":"0s", "precache": true},
"reverse_destinations": {"limit": 10000, "ttl":"0s", "precache": true},
"rating_plans": {"limit": 10000, "ttl":"0s","precache": true},
"rating_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"lcr_rules": {"limit": 10000, "ttl":"0s", "precache": true},
"cdr_stats": {"limit": 10000, "ttl":"0s", "precache": true},
"actions": {"limit": 10000, "ttl":"0s", "precache": true},
"action_plans": {"limit": 10000, "ttl":"0s", "precache": true},
"account_action_plans": {"limit": 10000, "ttl":"0s", "precache": true},
"action_triggers": {"limit": 10000, "ttl":"0s", "precache": true},
"shared_groups": {"limit": 10000, "ttl":"0s", "precache": true},
"aliases": {"limit": 10000, "ttl":"0s", "precache": true},
"reverse_aliases": {"limit": 10000, "ttl":"0s", "precache": true},
"derived_chargers": {"limit": 10000, "ttl":"0s", "precache": true},
"resource_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"resources": {"limit": 10000, "ttl":"0s", "precache": true},
"statqueues": {"limit": 10000, "ttl":"0s", "precache": true},
"statqueue_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"thresholds": {"limit": 10000, "ttl":"0s", "precache": true},
"threshold_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"filters": {"limit": 10000, "ttl":"0s", "precache": true},
"supplier_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"attribute_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"resource_filter_indexes" :{"limit": 10000, "ttl":"0s"},
"resource_filter_revindexes" : {"limit": 10000, "ttl":"0s"},
"stat_filter_indexes" : {"limit": 10000, "ttl":"0s"},
"stat_filter_revindexes" : {"limit": 10000, "ttl":"0s"},
"threshold_filter_indexes" : {"limit": 10000, "ttl":"0s"},
"threshold_filter_revindexes" : {"limit": 10000, "ttl":"0s"},
"supplier_filter_indexes" : {"limit": 10000, "ttl":"0s"},
"supplier_filter_revindexes" :{"limit": 10000, "ttl":"0s"},
"attribute_filter_indexes" : {"limit": 10000, "ttl":"0s"},
"attribute_filter_revindexes" : {"limit": 10000, "ttl":"0s"},
},
}

View File

@@ -0,0 +1,67 @@
{
// CGRateS Configuration file
//
"general": {
"log_level": 7,
"dbdata_encoding": "msgpack", // encoding used to store object data in strings: <msgpack|json>
},
"listen": {
"rpc_json": ":2012",
"rpc_gob": ":2013",
"http": ":2080",
},
"data_db": { // database used to store runtime data (eg: accounts, cdr stats)
"db_type": "redis", // data_db type: <redis|mongo>
"db_port": 6379, // data_db port to reach the database
"db_name": "11", // data_db database name to connect to
},
"stor_db": {
"db_password": "CGRateS.org",
},
"cache":{
"destinations": {"limit": 10000, "ttl":"0s", "precache": true},
"reverse_destinations": {"limit": 10000, "ttl":"0s", "precache": true},
"rating_plans": {"limit": 10000, "ttl":"0s","precache": true},
"rating_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"lcr_rules": {"limit": 10000, "ttl":"0s", "precache": true},
"cdr_stats": {"limit": 10000, "ttl":"0s", "precache": true},
"actions": {"limit": 10000, "ttl":"0s", "precache": true},
"action_plans": {"limit": 10000, "ttl":"0s", "precache": true},
"account_action_plans": {"limit": 10000, "ttl":"0s", "precache": true},
"action_triggers": {"limit": 10000, "ttl":"0s", "precache": true},
"shared_groups": {"limit": 10000, "ttl":"0s", "precache": true},
"aliases": {"limit": 10000, "ttl":"0s", "precache": true},
"reverse_aliases": {"limit": 10000, "ttl":"0s", "precache": true},
"derived_chargers": {"limit": 10000, "ttl":"0s", "precache": true},
"resource_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"resources": {"limit": 10000, "ttl":"0s", "precache": true},
"statqueues": {"limit": 10000, "ttl":"0s", "precache": true},
"statqueue_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"thresholds": {"limit": 10000, "ttl":"0s", "precache": true},
"threshold_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"filters": {"limit": 10000, "ttl":"0s", "precache": true},
"supplier_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"attribute_profiles": {"limit": 10000, "ttl":"0s", "precache": true},
"resource_filter_indexes" :{"limit": 10000, "ttl":"0s"},
"resource_filter_revindexes" : {"limit": 10000, "ttl":"0s"},
"stat_filter_indexes" : {"limit": 10000, "ttl":"0s"},
"stat_filter_revindexes" : {"limit": 10000, "ttl":"0s"},
"threshold_filter_indexes" : {"limit": 10000, "ttl":"0s"},
"threshold_filter_revindexes" : {"limit": 10000, "ttl":"0s"},
"supplier_filter_indexes" : {"limit": 10000, "ttl":"0s"},
"supplier_filter_revindexes" :{"limit": 10000, "ttl":"0s"},
"attribute_filter_indexes" : {"limit": 10000, "ttl":"0s"},
"attribute_filter_revindexes" : {"limit": 10000, "ttl":"0s"},
},
}

View File

@@ -4,61 +4,80 @@
// Copyright (C) ITsysCOM GmbH
"general": {
"http_skip_tls_verify": false, // if enabled Http Client will accept any TLS certificate
"rounding_decimals": 5, // system level precision for floats
"dbdata_encoding": "msgpack", // encoding used to store object data in strings: <msgpack|json>
"tpexport_dir": "/var/spool/cgrates/tpe", // path towards export folder for offline Tariff Plans
"httpposter_attempts": 3, // number of http attempts before considering request failed (eg: *call_url)
"default_request_type": "*rated", // default request type to consider when missing from requests: <""|*prepaid|*postpaid|*pseudoprepaid|*rated>
"default_category": "call", // default category to consider when missing from requests
"default_tenant": "cgrates.org", // default tenant to consider when missing from requests
"default_timezone": "Local", // default timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB>
"connect_attempts": 3, // initial server connect attempts
"reconnects": -1, // number of retries in case of connection lost
"connect_timeout": "1s", // consider connection unsuccessful on timeout, 0 to disable the feature
"reply_timeout": "2s", // consider connection down for replies taking longer than this value
"response_cache_ttl": "0s", // the life span of a cached response
"internal_ttl": "2m", // maximum duration to wait for internal connections before giving up
"locking_timeout": "5s", // timeout internal locks to avoid deadlocks
"cache_dump_dir": "", // cache dump for faster start (leave empty to disable)
"log_level": 7,
},
"rals": {
"enabled": true,
"cdrstats_conns": [
{"address": "*internal"}
],
"pubsubs_conns": [
{"address": "*internal"}
],
"users_conns": [
{"address": "*internal"}
],
"aliases_conns": [
{"address": "*internal"}
],
"listen": {
"rpc_json": ":2012",
"rpc_gob": ":2013",
"http": ":2080",
},
"stor_db": { // database used to store offline tariff plans and CDRs
"db_password": "CGRateS.org", // password to use when connecting to stordb
"stor_db": {
"db_password": "CGRateS.org",
},
"scheduler": {
"enabled": true,
},
"cdrs": {
"enabled": true, // start the CDR Server service: <true|false>
"cdrstats_conns": [
"rals": {
"enabled": true,
"thresholds_conns": [
{"address": "127.0.0.1:2012", "transport": "*json"}
],
"stats_conns": [
{"address": "127.0.0.1:2012", "transport": "*json"}
],
"pubsubs_conns": [
{"address": "*internal"}
],
"attributes_conns": [
{"address": "127.0.0.1:2012", "transport": "*json"}
],
},
"cdrstats": {
"enabled": true, // starts the cdrstats service: <true|false>
"cdrs": {
"enabled": true,
"sessions_conns": [
{"address": "127.0.0.1:2012", "transport": "*json"}
],
"stats_conns": [
{"address": "127.0.0.1:2012", "transport": "*json"}
],
"sessions_cost_retries": 5,
},
"sessions": {
"enabled": true,
"rals_conns": [
{"address": "127.0.0.1:2012", "transport": "*json"}
],
"cdrs_conns": [
{"address": "127.0.0.1:2012", "transport": "*json"}
],
"resources_conns": [
{"address": "127.0.0.1:2012", "transport": "*json"}
],
"suppliers_conns": [
{"address": "127.0.0.1:2012", "transport": "*json"}
],
"attributes_conns": [
{"address": "127.0.0.1:2012", "transport": "*json"}
],
"stats_conns": [
{"address": "127.0.0.1:2012", "transport": "*json"}
],
"thresholds_conns": [
{"address": "127.0.0.1:2012", "transport": "*json"}
],
"debit_interval": "10s",
},
@@ -125,33 +144,68 @@
},
"sessions": {
"enabled": true,
"debit_interval": "5s", // interval to perform debits on.
},
"sm_asterisk": {
"enabled": true, // starts Asterisk SessionManager service: <true|false>
"asterisk_agent": {
"enabled": true,
"sessions_conns": [
{"address": "*internal"}
],
"create_cdr": true,
"asterisk_conns":[ // instantiate connections to multiple Asterisk servers
"asterisk_conns":[
{"address": "127.0.0.1:8088", "user": "cgrates", "password": "CGRateS.org", "connect_attempts": 3,"reconnects": 10}
],
},
"pubsubs": {
"enabled": true, // starts PubSub service: <true|false>.
"enabled": true,
},
"aliases": {
"enabled": true, // starts PubSub service: <true|false>.
"attributes": {
"enabled": true,
"string_indexed_fields": ["Account"],
},
"users": {
"enabled": true, // starts User service: <true|false>.
"indexes": ["Uuid"], // user profile field indexes
"resources": {
"enabled": true,
"thresholds_conns": [
{"address": "*internal"}
],
"string_indexed_fields": ["Account"],
"prefix_indexed_fields": ["Destination"],
},
"stats": {
"enabled": true,
"thresholds_conns": [
{"address": "*internal"}
],
"string_indexed_fields": ["Account"],
},
"thresholds": {
"enabled": true,
"string_indexed_fields": ["Account"],
},
"suppliers": {
"enabled": true,
"rals_conns": [
{"address": "*internal"}
],
"resources_conns": [
{"address": "*internal"}
],
"stats_conns": [
{"address": "*internal"}
],
"string_indexed_fields": ["Account"],
"prefix_indexed_fields": ["Destination"],
},
}

View File

@@ -1,15 +1,13 @@
{
// Real-time Charging System for Telecom & ISP environments
// Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
// Copyright (C) ITsysCOM GmbH
//
// This file contains the default configuration hardcoded into CGRateS.
// This is what you get when you load CGRateS with an empty configuration file.
"general": {
"log_level": 7,
},
"listen": {
"rpc_json": ":2012",
"rpc_gob": ":2013",

View File

@@ -1,484 +0,0 @@
// +build calls
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package general_tests
import (
"net/rpc"
"net/rpc/jsonrpc"
"os"
"path"
"reflect"
"testing"
"time"
"github.com/cgrates/cgrates/apier/v1"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
const astPassword = "CGRateS.org"
var tutAstCallsCfg *config.CGRConfig
var tutAstCallsRpc *rpc.Client
var tutAstCallsPjSuaListener *os.File
func TestTutAstCallsInitCfg(t *testing.T) {
// Init config first
var err error
tutAstCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*dataDir, "tutorials", "asterisk_ari", "cgrates", "etc", "cgrates"))
if err != nil {
t.Error(err)
}
tutAstCallsCfg.DataFolderPath = *dataDir // Share DataFolderPath through config towards StoreDb for Flush()
config.SetCgrConfig(tutAstCallsCfg)
}
// Remove data in both rating and accounting db
func TestTutAstCallsResetDataDb(t *testing.T) {
if err := engine.InitDataDb(tutAstCallsCfg); err != nil {
t.Fatal(err)
}
}
// Wipe out the cdr database
func TestTutAstCallsResetStorDb(t *testing.T) {
if err := engine.InitStorDb(tutAstCallsCfg); err != nil {
t.Fatal(err)
}
}
// start Asterisk server
func TestTutAstCallsStartAsterisk(t *testing.T) {
engine.KillProcName("asterisk", 1000)
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "asterisk_ari", "asterisk", "etc", "init.d", "asterisk"), "start", 2000); err != nil {
t.Fatal(err)
}
}
// Start CGR Engine
func TestTutAstCallsStartEngine(t *testing.T) {
engine.KillProcName("cgr-engine", *waitRater)
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "asterisk_ari", "cgrates", "etc", "init.d", "cgrates"), "start", 100); err != nil {
t.Fatal(err)
}
}
/*
// Restart FS so we make sure reconnects are working
func TestTutAstCallsRestartAsterisk(t *testing.T) {
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "asterisk_ari", "asterisk", "etc", "init.d", "asterisk"), "restart", 2000); err != nil {
t.Fatal(err)
}
}
*/
// Connect rpc client to rater
func TestTutAstCallsRpcConn(t *testing.T) {
var err error
tutAstCallsRpc, err = jsonrpc.Dial("tcp", tutAstCallsCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
if err != nil {
t.Fatal(err)
}
}
// Load the tariff plan, creating accounts and their balances
func TestTutAstCallsLoadTariffPlanFromFolder(t *testing.T) {
reply := ""
attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial")}
if err := tutAstCallsRpc.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil {
t.Error(err)
} else if reply != utils.OK {
t.Error(reply)
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups
}
// Make sure account was debited properly
func TestTutAstCallsAccountsBefore(t *testing.T) {
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutAstCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1002"}
if err := tutAstCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1003"}
if err := tutAstCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1004"}
if err := tutAstCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1007"}
if err := tutAstCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 0.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1005"}
if err := tutAstCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() {
t.Error("Got error on ApierV2.GetAccount: %v", err)
}
}
// Make sure all stats queues are in place
func TestTutAstCallsCdrStatsBefore(t *testing.T) {
//eQueueIds := []string{"*default", "CDRST1", "CDRST_1001", "CDRST_1002", "CDRST_1003", "STATS_SUPPL1", "STATS_SUPPL2"}
var statMetrics map[string]float64
eMetrics := map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutAstCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST1"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
eMetrics = map[string]float64{engine.ACC: -1, engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1}
if err := tutAstCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1001"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutAstCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1002"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutAstCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1003"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutAstCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "STATS_SUPPL1"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutAstCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "STATS_SUPPL2"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
}
// Start Pjsua as listener and register it to receive calls
func TestTutAstCallsStartPjsuaListener(t *testing.T) {
var err error
acnts := []*engine.PjsuaAccount{
&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: astPassword, Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1002@127.0.0.1", Username: "1002", Password: astPassword, Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1003@127.0.0.1", Username: "1003", Password: astPassword, Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1004@127.0.0.1", Username: "1004", Password: astPassword, Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1006@127.0.0.1", Username: "1006", Password: astPassword, Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1007@127.0.0.1", Username: "1007", Password: astPassword, Realm: "*", Registrar: "sip:127.0.0.1:5060"}}
if tutAstCallsPjSuaListener, err = engine.StartPjsuaListener(acnts, 5070, time.Duration(500*time.Millisecond)); err != nil {
t.Fatal(err)
}
}
// Call from 1001 (prepaid) to 1002
func TestTutAstCallsCall1001To1002(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: astPassword, Realm: "*"}, "sip:1002@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(67)*time.Second, 5071); err != nil {
t.Fatal(err)
}
}
// Call from 1001 (prepaid) to 1003
func TestTutAstCallsCall1001To1003(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: astPassword, Realm: "*"}, "sip:1003@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(65)*time.Second, 5072); err != nil {
t.Fatal(err)
}
}
func TestTutAstCallsCall1002To1001(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1002@127.0.0.1", Username: "1002", Password: astPassword, Realm: "*"}, "sip:1001@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(61)*time.Second, 5073); err != nil {
t.Fatal(err)
}
}
func TestTutAstCallsCall1003To1001(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1003@127.0.0.1", Username: "1003", Password: astPassword, Realm: "*"}, "sip:1001@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(63)*time.Second, 5074); err != nil {
t.Fatal(err)
}
}
func TestTutAstCallsCall1004To1001(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1004@127.0.0.1", Username: "1004", Password: astPassword, Realm: "*"}, "sip:1001@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(62)*time.Second, 5075); err != nil {
t.Fatal(err)
}
}
func TestTutAstCallsCall1006To1002(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1006@127.0.0.1", Username: "1006", Password: astPassword, Realm: "*"}, "sip:1002@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(64)*time.Second, 5076); err != nil {
t.Fatal(err)
}
}
func TestTutAstCallsCall1007To1002(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1007@127.0.0.1", Username: "1007", Password: astPassword, Realm: "*"}, "sip:1002@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(66)*time.Second, 5077); err != nil {
t.Fatal(err)
}
}
// Make sure account was debited properly
func TestTutAstCallsAccount1001(t *testing.T) {
time.Sleep(time.Duration(70) * time.Second) // Allow calls to finish before start querying the results
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutAstCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() == 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
} else if reply.Disabled == true {
t.Error("Account disabled")
}
}
// Make sure account was debited properly
func TestTutAstCalls1001Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
//var cgrId string // Share with getCostDetails
//var cCost engine.CallCost
req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, Accounts: []string{"1001"}, DestinationPrefixes: []string{"1002"}}
if err := tutAstCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
//cgrId = reply[0].CGRID
if reply[0].Source != "OSIPS_E_ACC_EVENT" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Usage != "67" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
if reply[0].Cost == -1.0 { // Cost was not calculated
t.Errorf("Unexpected Cost for CDR: %+v", reply[0])
}
//if reply[0].Supplier != "suppl2" { // Usage as seconds
// t.Errorf("Unexpected Supplier for CDR: %+v", reply[0])
//}
}
req = utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, Accounts: []string{"1001"}, DestinationPrefixes: []string{"1003"}}
if err := tutAstCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
//cgrId = reply[0].CGRID
if reply[0].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Usage != "65" && reply[0].Usage != "66" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
if reply[0].Cost != 0 { // Cost was not calculated
t.Errorf("Unexpected Cost for CDR: %+v", reply[0])
}
}
req = utils.RPCCDRsFilter{Accounts: []string{"1001"}, RunIDs: []string{"derived_run1"}}
if err := tutAstCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 2 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].RequestType != utils.META_RATED {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Subject != "1002" {
t.Errorf("Unexpected Subject for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutAstCalls1002Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1002"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutAstCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 2 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "OSIPS_E_ACC_EVENT" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_POSTPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "61" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutAstCalls1003Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1003"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutAstCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "OSIPS_E_ACC_EVENT" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PSEUDOPREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "63" && reply[0].Usage != "64" { // Usage as seconds, sometimes takes a second longer to disconnect
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutAstCalls1004Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1004"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutAstCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "OSIPS_E_ACC_EVENT" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_RATED {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "62" && reply[0].Usage != "63" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
}
}
// Make sure aliasing was done for 1006 and we have no CDRs for it
func TestTutAstCalls1006Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1006"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutAstCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 0 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
}
}
// Make sure account was debited properly
func TestTutAstCalls1007Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1007"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutAstCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "OSIPS_E_ACC_EVENT" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1002" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "66" && reply[0].Usage != "67" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
if reply[0].Cost == -1.0 { // Cost was not calculated
t.Errorf("Unexpected Cost for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutAstCallsAccountFraud1001(t *testing.T) {
var reply string
attrAddBlnc := &v1.AttrAddBalance{Tenant: "cgrates.org", Account: "1001", BalanceType: "*monetary", Value: 101}
if err := tutAstCallsRpc.Call("ApierV1.AddBalance", attrAddBlnc, &reply); err != nil {
t.Error("Got error on ApierV1.AddBalance: ", err.Error())
} else if reply != "OK" {
t.Errorf("Calling ApierV1.AddBalance received: %s", reply)
}
}
// Based on Fraud automatic mitigation, our account should be disabled
func TestTutAstCallsAccountDisabled1001(t *testing.T) {
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutAstCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.Disabled == false {
t.Error("Account should be disabled per fraud detection rules.")
}
}
func TestTutAstCallsStopPjsuaListener(t *testing.T) {
time.Sleep(70 * time.Second) // Give time for calls to go through before unregistering
tutAstCallsPjSuaListener.Write([]byte("q\n")) // Close pjsua
time.Sleep(time.Duration(1) * time.Second) // Allow pjsua to finish it's tasks, eg un-REGISTER
}
func TestTutAstCallsStopCgrEngine(t *testing.T) {
if err := engine.KillEngine(100); err != nil {
t.Error(err)
}
}
func TestTutAstCallsStopAsterisk(t *testing.T) {
engine.KillProcName("asterisk", 100)
}

View File

@@ -1,4 +1,4 @@
// +build newcall
// +build call
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
@@ -22,7 +22,6 @@ package general_tests
import (
"flag"
"fmt"
"net/rpc"
"net/rpc/jsonrpc"
"os"
@@ -37,14 +36,15 @@ import (
"github.com/cgrates/cgrates/utils"
)
var tutFsCallsCfg *config.CGRConfig
var tutFsCallsRpc *rpc.Client
var tutFsCallsPjSuaListener *os.File
var tutorialCallsCfg *config.CGRConfig
var tutorialCallsRpc *rpc.Client
var tutorialCallsPjSuaListener *os.File
var waitRater = flag.Int("wait_rater", 100, "Number of miliseconds to wait for rater to start and cache")
var dataDir = flag.String("data_dir", "/usr/share/cgrates", "CGR data dir path here")
var fsConfig = flag.String("fsConfig", "/usr/share/cgrates/tutorials/fs_evsock", "FreeSwitch tutorial folder")
var kamConfig = flag.String("kamConfig", "/usr/share/cgrates/tutorials/kamevapi", "Kamailio tutorial folder")
var oSipsConfig = flag.String("osConfig", "/usr/share/cgrates/tutorials/osips_native", "OpenSips tutorial folder")
var ariConf = flag.String("ariConf", "/usr/share/cgrates/tutorials/asterisk_ari", "Asterisk tutorial folder")
var optConf string
var sTestsCalls = []func(t *testing.T){
@@ -80,61 +80,75 @@ var sTestsCalls = []func(t *testing.T){
}
//Test start here
func TestFSCalls(t *testing.T) {
func TestFreeswitchCalls(t *testing.T) {
optConf = utils.Freeswitch
for _, stest := range sTestsCalls {
t.Run("", stest)
}
}
func TestKamCalls(t *testing.T) {
func TestKamailioCalls(t *testing.T) {
optConf = utils.Kamailio
for _, stest := range sTestsCalls {
t.Run("", stest)
}
}
func TestOSCalls(t *testing.T) {
func TestOpensipsCalls(t *testing.T) {
optConf = utils.Opensips
for _, stest := range sTestsCalls {
t.Run("", stest)
}
}
/* Need to be checked
func TestAsteriskCalls(t *testing.T) {
optConf = utils.Asterisk
for _, stest := range sTestsCalls {
t.Run("", stest)
}
}
*/
func testCallInitCfg(t *testing.T) {
// Init config first
var err error
if optConf == utils.Freeswitch {
tutFsCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*fsConfig, "cgrates", "etc", "cgrates"))
tutorialCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*fsConfig, "cgrates", "etc", "cgrates"))
if err != nil {
t.Error(err)
}
} else if optConf == utils.Kamailio {
tutFsCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*kamConfig, "cgrates", "etc", "cgrates"))
tutorialCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*kamConfig, "cgrates", "etc", "cgrates"))
if err != nil {
t.Error(err)
}
} else if optConf == utils.Opensips {
tutFsCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*oSipsConfig, "cgrates", "etc", "cgrates"))
tutorialCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*oSipsConfig, "cgrates", "etc", "cgrates"))
if err != nil {
t.Error(err)
}
} else if optConf == utils.Asterisk {
tutorialCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*ariConf, "cgrates", "etc", "cgrates"))
if err != nil {
t.Error(err)
}
}
tutFsCallsCfg.DataFolderPath = *dataDir // Share DataFolderPath through config towards StoreDb for Flush()
config.SetCgrConfig(tutFsCallsCfg)
tutorialCallsCfg.DataFolderPath = *dataDir // Share DataFolderPath through config towards StoreDb for Flush()
config.SetCgrConfig(tutorialCallsCfg)
}
// Remove data in both rating and accounting db
func testCallResetDataDb(t *testing.T) {
if err := engine.InitDataDb(tutFsCallsCfg); err != nil {
if err := engine.InitDataDb(tutorialCallsCfg); err != nil {
t.Fatal(err)
}
}
// Wipe out the cdr database
func testCallResetStorDb(t *testing.T) {
if err := engine.InitStorDb(tutFsCallsCfg); err != nil {
if err := engine.InitStorDb(tutorialCallsCfg); err != nil {
t.Fatal(err)
}
}
@@ -156,6 +170,11 @@ func testCallStartFS(t *testing.T) {
if err := engine.CallScript(path.Join(*oSipsConfig, "opensips", "etc", "init.d", "opensips"), "start", 3000); err != nil {
t.Fatal(err)
}
} else if optConf == utils.Asterisk {
engine.KillProcName(utils.Asterisk, 5000)
if err := engine.CallScript(path.Join(*ariConf, "asterisk", "etc", "init.d", "asterisk"), "start", 3000); err != nil {
t.Fatal(err)
}
}
}
@@ -174,6 +193,10 @@ func testCallStartEngine(t *testing.T) {
if err := engine.CallScript(path.Join(*oSipsConfig, "cgrates", "etc", "init.d", "cgrates"), "start", 100); err != nil {
t.Fatal(err)
}
} else if optConf == utils.Asterisk {
if err := engine.CallScript(path.Join(*ariConf, "cgrates", "etc", "init.d", "cgrates"), "start", 100); err != nil {
t.Fatal(err)
}
}
}
@@ -191,13 +214,17 @@ func testCallRestartFS(t *testing.T) {
if err := engine.CallScript(path.Join(*oSipsConfig, "opensips", "etc", "init.d", "opensips"), "restart", 5000); err != nil {
t.Fatal(err)
}
} else if optConf == utils.Asterisk {
if err := engine.CallScript(path.Join(*ariConf, "asterisk", "etc", "init.d", "asterisk"), "restart", 5000); err != nil {
t.Fatal(err)
}
}
}
// Connect rpc client to rater
func testCallRpcConn(t *testing.T) {
var err error
tutFsCallsRpc, err = jsonrpc.Dial("tcp", tutFsCallsCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
tutorialCallsRpc, err = jsonrpc.Dial("tcp", tutorialCallsCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
if err != nil {
t.Fatal(err)
}
@@ -208,7 +235,7 @@ func testCallRpcConn(t *testing.T) {
func testCallLoadTariffPlanFromFolder(t *testing.T) {
var reply string
attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial2")}
if err := tutFsCallsRpc.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil {
if err := tutorialCallsRpc.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil {
t.Error(err)
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups
@@ -218,14 +245,14 @@ func testCallLoadTariffPlanFromFolder(t *testing.T) {
func testCallAccountsBefore(t *testing.T) {
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
if err := tutorialCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 {
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
var reply2 *engine.Account
attrs2 := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1002"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs2, &reply2); err != nil {
if err := tutorialCallsRpc.Call("ApierV2.GetAccount", attrs2, &reply2); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply2.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 {
t.Errorf("Calling ApierV1.GetBalance received: %f", reply2.BalanceMap[utils.MONETARY].GetTotalValue())
@@ -238,7 +265,7 @@ func testCallStatMetricsBefore(t *testing.T) {
utils.MetaTCC: utils.NOT_AVAILABLE,
utils.MetaTCD: utils.NOT_AVAILABLE,
}
if err := tutFsCallsRpc.Call(utils.StatSv1GetQueueStringMetrics,
if err := tutorialCallsRpc.Call(utils.StatSv1GetQueueStringMetrics,
&utils.TenantID{Tenant: "cgrates.org", ID: "Stats2"}, &metrics); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(expectedMetrics, metrics) {
@@ -257,7 +284,7 @@ func testCallCheckResourceBeforeAllocation(t *testing.T) {
utils.Subject: "1001",
utils.Destination: "1002"},
}}
if err := tutFsCallsRpc.Call(utils.ResourceSv1GetResourcesForEvent, args, &rs); err != nil {
if err := tutorialCallsRpc.Call(utils.ResourceSv1GetResourcesForEvent, args, &rs); err != nil {
t.Error(err)
} else if len(*rs) != 2 {
t.Errorf("Resources: %+v", utils.ToJSON(rs))
@@ -273,7 +300,7 @@ func testCallCheckResourceBeforeAllocation(t *testing.T) {
func testCallCheckThreshold1001Before(t *testing.T) {
var td engine.Threshold
eTd := engine.Threshold{Tenant: "cgrates.org", ID: "THD_ACNT_1001", Hits: 0}
if err := tutFsCallsRpc.Call(utils.ThresholdSv1GetThreshold,
if err := tutorialCallsRpc.Call(utils.ThresholdSv1GetThreshold,
&utils.TenantID{Tenant: "cgrates.org", ID: "THD_ACNT_1001"}, &td); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eTd, td) {
@@ -284,7 +311,7 @@ func testCallCheckThreshold1001Before(t *testing.T) {
func testCallCheckThreshold1002Before(t *testing.T) {
var td engine.Threshold
eTd := engine.Threshold{Tenant: "cgrates.org", ID: "THD_ACNT_1002", Hits: 0}
if err := tutFsCallsRpc.Call(utils.ThresholdSv1GetThreshold,
if err := tutorialCallsRpc.Call(utils.ThresholdSv1GetThreshold,
&utils.TenantID{Tenant: "cgrates.org", ID: "THD_ACNT_1002"}, &td); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eTd, td) {
@@ -303,7 +330,7 @@ func testCallStartPjsuaListener(t *testing.T) {
&engine.PjsuaAccount{Id: "sip:1003@127.0.0.1",
Username: "1003", Password: "CGRateS.org", Realm: "*", Registrar: "sip:127.0.0.1:5080"},
}
if tutFsCallsPjSuaListener, err = engine.StartPjsuaListener(
if tutorialCallsPjSuaListener, err = engine.StartPjsuaListener(
acnts, 5070, time.Duration(*waitRater)*time.Millisecond); err != nil {
t.Fatal(err)
}
@@ -332,7 +359,7 @@ func testCallGetActiveSessions(t *testing.T) {
Destination: "1002",
},
}
if err := tutFsCallsRpc.Call(utils.SessionSv1GetActiveSessions,
if err := tutorialCallsRpc.Call(utils.SessionSv1GetActiveSessions,
&map[string]string{}, &reply); err != nil {
t.Error("Got error on SessionSv1.GetActiveSessions: ", err.Error())
} else {
@@ -377,7 +404,7 @@ func testCallCheckResourceAllocation(t *testing.T) {
utils.Subject: "1001",
utils.Destination: "1002"},
}}
if err := tutFsCallsRpc.Call(utils.ResourceSv1GetResourcesForEvent, args, &rs); err != nil {
if err := tutorialCallsRpc.Call(utils.ResourceSv1GetResourcesForEvent, args, &rs); err != nil {
t.Error(err)
} else if len(*rs) != 2 {
t.Errorf("Resources: %+v", utils.ToJSON(rs))
@@ -395,7 +422,7 @@ func testCallAccount1001(t *testing.T) {
time.Sleep(time.Duration(80) * time.Second) // Allow calls to finish before start querying the results
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
if err := tutorialCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error(err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() == 10.0 { // Make sure we debitted
t.Errorf("Expected: 10, received: %+v", reply.BalanceMap[utils.MONETARY].GetTotalValue())
@@ -408,15 +435,12 @@ func testCallAccount1001(t *testing.T) {
func testCall1001Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, Accounts: []string{"1001"}}
if err := tutFsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
if err := tutorialCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 2 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
for _, cdr := range reply {
if cdr.Source != "freeswitch_json" && cdr.Source != "SMG_KamailioAgent" {
t.Errorf("Unexpected Source for CDR: %+v", cdr.Source)
}
if cdr.RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", cdr.RequestType)
}
@@ -443,14 +467,11 @@ func testCall1001Cdrs(t *testing.T) {
func testCall1002Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, Accounts: []string{"1002"}, DestinationPrefixes: []string{"1001"}}
if err := tutFsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
if err := tutorialCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "freeswitch_json" && reply[0].Source != "SMG_KamailioAgent" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0].Source)
}
if reply[0].RequestType != utils.META_POSTPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0].RequestType)
}
@@ -473,7 +494,7 @@ func testCallStatMetrics(t *testing.T) {
utils.MetaTCC: "1.34009",
utils.MetaTCD: "2m24s",
}
if err := tutFsCallsRpc.Call(utils.StatSv1GetQueueStringMetrics,
if err := tutorialCallsRpc.Call(utils.StatSv1GetQueueStringMetrics,
&utils.TenantID{Tenant: "cgrates.org", ID: "Stats2"}, &metrics); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(expectedMetrics1, metrics) &&
@@ -493,7 +514,7 @@ func testCallCheckResourceRelease(t *testing.T) {
utils.Subject: "1001",
utils.Destination: "1002"},
}}
if err := tutFsCallsRpc.Call(utils.ResourceSv1GetResourcesForEvent, args, &rs); err != nil {
if err := tutorialCallsRpc.Call(utils.ResourceSv1GetResourcesForEvent, args, &rs); err != nil {
t.Error(err)
} else if len(*rs) != 2 {
t.Errorf("Resources: %+v", rs)
@@ -508,7 +529,7 @@ func testCallCheckResourceRelease(t *testing.T) {
func testCallCheckThreshold1001After(t *testing.T) {
var td engine.Threshold
if err := tutFsCallsRpc.Call(utils.ThresholdSv1GetThreshold,
if err := tutorialCallsRpc.Call(utils.ThresholdSv1GetThreshold,
&utils.TenantID{Tenant: "cgrates.org", ID: "THD_ACNT_1001"}, &td); err != nil &&
err.Error() != utils.ErrNotFound.Error() {
t.Error(err)
@@ -518,7 +539,7 @@ func testCallCheckThreshold1001After(t *testing.T) {
func testCallCheckThreshold1002After(t *testing.T) {
var td engine.Threshold
eTd := engine.Threshold{Tenant: "cgrates.org", ID: "THD_ACNT_1002", Hits: 4}
if err := tutFsCallsRpc.Call(utils.ThresholdSv1GetThreshold,
if err := tutorialCallsRpc.Call(utils.ThresholdSv1GetThreshold,
&utils.TenantID{Tenant: "cgrates.org", ID: "THD_ACNT_1002"}, &td); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eTd.Tenant, td.Tenant) {
@@ -531,8 +552,8 @@ func testCallCheckThreshold1002After(t *testing.T) {
}
func testCallStopPjsuaListener(t *testing.T) {
tutFsCallsPjSuaListener.Write([]byte("q\n")) // Close pjsua
time.Sleep(time.Duration(1) * time.Second) // Allow pjsua to finish it's tasks, eg un-REGISTER
tutorialCallsPjSuaListener.Write([]byte("q\n")) // Close pjsua
time.Sleep(time.Duration(1) * time.Second) // Allow pjsua to finish it's tasks, eg un-REGISTER
}
func testCallStopCgrEngine(t *testing.T) {
@@ -548,5 +569,7 @@ func testCallStopFS(t *testing.T) {
engine.KillProcName(utils.Kamailio, 1000)
} else if optConf == utils.Opensips {
engine.KillProcName(utils.Opensips, 1000)
} else if optConf == utils.Asterisk {
engine.KillProcName(utils.Asterisk, 1000)
}
}

View File

@@ -1,497 +0,0 @@
// +build calls
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package general_tests
import (
"net/rpc"
"net/rpc/jsonrpc"
"os"
"path"
"reflect"
"testing"
"time"
"github.com/cgrates/cgrates/apier/v1"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
var tutFsCallsCfg *config.CGRConfig
var tutFsCallsRpc *rpc.Client
var tutFsCallsPjSuaListener *os.File
func TestTutFsCallsInitCfg(t *testing.T) {
// Init config first
var err error
tutFsCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*dataDir, "tutorials", "fs_evsock", "cgrates", "etc", "cgrates"))
if err != nil {
t.Error(err)
}
tutFsCallsCfg.DataFolderPath = *dataDir // Share DataFolderPath through config towards StoreDb for Flush()
config.SetCgrConfig(tutFsCallsCfg)
}
// Remove data in both rating and accounting db
func TestTutFsCallsResetDataDb(t *testing.T) {
if err := engine.InitDataDb(tutFsCallsCfg); err != nil {
t.Fatal(err)
}
}
// Wipe out the cdr database
func TestTutFsCallsResetStorDb(t *testing.T) {
if err := engine.InitStorDb(tutFsCallsCfg); err != nil {
t.Fatal(err)
}
}
// start FS server
func TestTutFsCallsStartFS(t *testing.T) {
engine.KillProcName("freeswitch", 5000)
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "fs_evsock", "freeswitch", "etc", "init.d", "freeswitch"), "start", 3000); err != nil {
t.Fatal(err)
}
}
// Start CGR Engine
func TestTutFsCallsStartEngine(t *testing.T) {
engine.KillProcName("cgr-engine", *waitRater)
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "fs_evsock", "cgrates", "etc", "init.d", "cgrates"), "start", 100); err != nil {
t.Fatal(err)
}
}
// Restart FS so we make sure reconnects are working
func TestTutFsCallsRestartFS(t *testing.T) {
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "fs_evsock", "freeswitch", "etc", "init.d", "freeswitch"), "restart", 5000); err != nil {
t.Fatal(err)
}
}
// Connect rpc client to rater
func TestTutFsCallsRpcConn(t *testing.T) {
var err error
tutFsCallsRpc, err = jsonrpc.Dial("tcp", tutFsCallsCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
if err != nil {
t.Fatal(err)
}
}
// Load the tariff plan, creating accounts and their balances
func TestTutFsCallsLoadTariffPlanFromFolder(t *testing.T) {
reply := ""
attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial")}
if err := tutFsCallsRpc.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil {
t.Error(err)
} else if reply != "OK" {
t.Error(reply)
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups
}
// Make sure account was debited properly
func TestTutFsCallsAccountsBefore(t *testing.T) {
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1002"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1003"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1004"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1007"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 0.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1005"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() {
t.Errorf("Got error on ApierV2.GetAccount: %v", err)
}
}
// Make sure all stats queues are in place
func TestTutFsCallsCdrStatsBefore(t *testing.T) {
//eQueueIds := []string{"*default", "CDRST1", "CDRST_1001", "CDRST_1002", "CDRST_1003", "STATS_SUPPL1", "STATS_SUPPL2"}
var statMetrics map[string]float64
eMetrics := map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutFsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST1"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
eMetrics = map[string]float64{engine.ACC: -1, engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1}
if err := tutFsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1001"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutFsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1002"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutFsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1003"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutFsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "STATS_SUPPL1"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutFsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "STATS_SUPPL2"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
}
// Start Pjsua as listener and register it to receive calls
func TestTutFsCallsStartPjsuaListener(t *testing.T) {
var err error
acnts := []*engine.PjsuaAccount{
&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1002@127.0.0.1", Username: "1002", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1003@127.0.0.1", Username: "1003", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1004@127.0.0.1", Username: "1004", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1006@127.0.0.1", Username: "1006", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1007@127.0.0.1", Username: "1007", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"}}
if tutFsCallsPjSuaListener, err = engine.StartPjsuaListener(acnts, 5070, time.Duration(*waitRater)*time.Millisecond); err != nil {
t.Fatal(err)
}
}
// Call from 1001 (prepaid) to 1002
func TestTutFsCallsCall1001To1002(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "1234", Realm: "*"}, "sip:1002@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(67)*time.Second, 5071); err != nil {
t.Fatal(err)
}
}
// Call from 1001 (prepaid) to 1003
func TestTutFsCallsCall1001To1003(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "1234", Realm: "*"}, "sip:1003@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(65)*time.Second, 5072); err != nil {
t.Fatal(err)
}
}
func TestTutFsCallsCall1002To1001(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1002@127.0.0.1", Username: "1002", Password: "1234", Realm: "*"}, "sip:1001@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(61)*time.Second, 5073); err != nil {
t.Fatal(err)
}
}
func TestTutFsCallsCall1003To1001(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1003@127.0.0.1", Username: "1003", Password: "1234", Realm: "*"}, "sip:1001@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(63)*time.Second, 5074); err != nil {
t.Fatal(err)
}
}
func TestTutFsCallsCall1004To1001(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1004@127.0.0.1", Username: "1004", Password: "1234", Realm: "*"}, "sip:1001@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(62)*time.Second, 5075); err != nil {
t.Fatal(err)
}
}
func TestTutFsCallsCall1006To1002(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1006@127.0.0.1", Username: "1006", Password: "1234", Realm: "*"}, "sip:1002@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(64)*time.Second, 5076); err != nil {
t.Fatal(err)
}
}
func TestTutFsCallsCall1007To1002(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1007@127.0.0.1", Username: "1007", Password: "1234", Realm: "*"}, "sip:1002@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(66)*time.Second, 5077); err != nil {
t.Fatal(err)
}
}
// Make sure account was debited properly
func TestTutFsCallsAccount1001(t *testing.T) {
time.Sleep(time.Duration(80) * time.Second) // Allow calls to finish before start querying the results
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() == 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
} else if reply.Disabled == true {
t.Error("Account disabled")
}
}
// Make sure account was debited properly
func TestTutFsCalls1001Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
//var CGRID string // Share with getCostDetails
//var cCost engine.CallCost
req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, Accounts: []string{"1001"}, DestinationPrefixes: []string{"1002"}}
if err := tutFsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
//CGRID = reply[0].CGRID
if reply[0].Source != "freeswitch_json" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Usage != "67" && reply[0].Usage != "68" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
if reply[0].Cost == -1.0 { // Cost was not calculated
t.Errorf("Unexpected Cost for CDR: %+v", reply[0])
}
//if reply[0].Supplier != "suppl2" { // Usage as seconds
// t.Errorf("Unexpected Supplier for CDR: %+v", reply[0])
//}
}
/*
// Make sure call cost contains the matched information
if err := tutFsCallsRpc.Call("ApierV2.GetCallCostLog", utils.AttrGetCallCost{CgrId: CGRID}, &cCost); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if utils.IsSliceMember([]string{cCost.Timespans[0].MatchedSubject, cCost.Timespans[0].MatchedPrefix, cCost.Timespans[0].MatchedDestId}, "") {
t.Errorf("Unexpected Matched* for CallCost: %+v", cCost.Timespans[0])
}
*/
req = utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, Accounts: []string{"1001"}, DestinationPrefixes: []string{"1003"}}
if err := tutFsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
//CGRID = reply[0].CGRID
if reply[0].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Usage != "65" && reply[0].Usage != "66" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
if reply[0].Cost != 0 { // Cost was not calculated
t.Errorf("Unexpected Cost for CDR: %+v", reply[0])
}
}
/*
// Make sure call cost contains the matched information
if err := tutFsCallsRpc.Call("ApierV2.GetCallCostLog", utils.AttrGetCallCost{CgrId: CGRID}, &cCost); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if utils.IsSliceMember([]string{cCost.Timespans[0].MatchedSubject, cCost.Timespans[0].MatchedPrefix, cCost.Timespans[0].MatchedDestId}, "") {
t.Errorf("Unexpected Matched* for CallCost: %+v", cCost.Timespans[0])
}
*/
req = utils.RPCCDRsFilter{Accounts: []string{"1001"}, RunIDs: []string{"derived_run1"}}
if err := tutFsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 2 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].RequestType != utils.META_RATED {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Subject != "1002" {
t.Errorf("Unexpected Subject for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutFsCalls1002Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1002"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutFsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 2 { // Should be counted here also call originated form 1006 which is aliased to 1002
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "freeswitch_json" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_POSTPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "61" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutFsCalls1003Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1003"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutFsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "freeswitch_json" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PSEUDOPREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "63" && reply[0].Usage != "64" { // Usage as seconds, sometimes takes a second longer to disconnect
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutFsCalls1004Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1004"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutFsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "freeswitch_json" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_RATED {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "62" && reply[0].Usage != "63" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
}
}
// Make sure we don't have any CDRs for 1006 since it should have been aliased to 1002
func TestTutFsCalls1006Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1006"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutFsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 0 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
}
}
// Make sure account was debited properly
func TestTutFsCalls1007Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1007"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutFsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "freeswitch_json" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1002" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "66" && reply[0].Usage != "67" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
if reply[0].Cost == -1.0 { // Cost was not calculated
t.Errorf("Unexpected Cost for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutFsCallsAccountFraud1001(t *testing.T) {
var reply string
attrAddBlnc := &v1.AttrAddBalance{Tenant: "cgrates.org", Account: "1001", BalanceType: "*monetary", Value: 101}
if err := tutFsCallsRpc.Call("ApierV1.AddBalance", attrAddBlnc, &reply); err != nil {
t.Error("Got error on ApierV1.AddBalance: ", err.Error())
} else if reply != "OK" {
t.Errorf("Calling ApierV1.AddBalance received: %s", reply)
}
}
// Based on Fraud automatic mitigation, our account should be disabled
func TestTutFsCallsAccountDisabled1001(t *testing.T) {
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.Disabled == false {
t.Error("Account should be disabled per fraud detection rules.")
}
}
func TestTutFsCallsStopPjsuaListener(t *testing.T) {
tutFsCallsPjSuaListener.Write([]byte("q\n")) // Close pjsua
time.Sleep(time.Duration(1) * time.Second) // Allow pjsua to finish it's tasks, eg un-REGISTER
}
func TestTutFsCallsStopCgrEngine(t *testing.T) {
if err := engine.KillEngine(100); err != nil {
t.Error(err)
}
}
func TestTutFsCallsStopFS(t *testing.T) {
engine.KillProcName("freeswitch", 1000)
}

View File

@@ -1,496 +0,0 @@
// +build calls
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package general_tests
import (
"net/rpc"
"net/rpc/jsonrpc"
"os"
"path"
"reflect"
"testing"
"time"
"github.com/cgrates/cgrates/apier/v1"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
var tutKamCallsCfg *config.CGRConfig
var tutKamCallsRpc *rpc.Client
var tutKamCallsPjSuaListener *os.File
func TestTutKamCallsInitCfg(t *testing.T) {
// Init config first
var err error
tutKamCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*dataDir, "tutorials", "kamevapi", "cgrates", "etc", "cgrates"))
if err != nil {
t.Error(err)
}
tutKamCallsCfg.DataFolderPath = *dataDir // Share DataFolderPath through config towards StoreDb for Flush()
config.SetCgrConfig(tutKamCallsCfg)
}
// Remove data in both rating and accounting db
func TestTutKamCallsResetDataDb(t *testing.T) {
if err := engine.InitDataDb(tutKamCallsCfg); err != nil {
t.Fatal(err)
}
}
// Wipe out the cdr database
func TestTutKamCallsResetStorDb(t *testing.T) {
if err := engine.InitStorDb(tutKamCallsCfg); err != nil {
t.Fatal(err)
}
}
// start FS server
func TestTutKamCallsStartKamailio(t *testing.T) {
engine.KillProcName("kamailio", 3000)
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "kamevapi", "kamailio", "etc", "init.d", "kamailio"), "start", 2000); err != nil {
t.Fatal(err)
}
}
// Start CGR Engine
func TestTutKamCallsStartEngine(t *testing.T) {
engine.KillProcName("cgr-engine", *waitRater)
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "kamevapi", "cgrates", "etc", "init.d", "cgrates"), "start", 100); err != nil {
t.Fatal(err)
}
}
// Restart FS so we make sure reconnects are working
func TestTutKamCallsRestartKamailio(t *testing.T) {
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "kamevapi", "kamailio", "etc", "init.d", "kamailio"), "restart", 3000); err != nil {
t.Fatal(err)
}
}
// Connect rpc client to rater
func TestTutKamCallsRpcConn(t *testing.T) {
var err error
tutKamCallsRpc, err = jsonrpc.Dial("tcp", tutKamCallsCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
if err != nil {
t.Fatal(err)
}
}
// Load the tariff plan, creating accounts and their balances
func TestTutKamCallsLoadTariffPlanFromFolder(t *testing.T) {
reply := ""
attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial")}
if err := tutKamCallsRpc.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil {
t.Error(err)
} else if reply != "OK" {
t.Error(reply)
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups
}
// Make sure account was debited properly
func TestTutKamCallsAccountsBefore(t *testing.T) {
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutKamCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1002"}
if err := tutKamCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1003"}
if err := tutKamCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1004"}
if err := tutKamCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1007"}
if err := tutKamCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 0.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1005"}
if err := tutKamCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() {
t.Errorf("Got error on ApierV2.GetAccount: %v", err)
}
}
// Make sure all stats queues are in place
func TestTutKamCallsCdrStatsBefore(t *testing.T) {
//eQueueIds := []string{"*default", "CDRST1", "CDRST_1001", "CDRST_1002", "CDRST_1003", "STATS_SUPPL1", "STATS_SUPPL2"}
var statMetrics map[string]float64
eMetrics := map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST1"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
eMetrics = map[string]float64{engine.ACC: -1, engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1}
if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1001"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1002"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1003"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "STATS_SUPPL1"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "STATS_SUPPL2"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
}
// Start Pjsua as listener and register it to receive calls
func TestTutKamCallsStartPjsuaListener(t *testing.T) {
var err error
acnts := []*engine.PjsuaAccount{
&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "CGRateS.org", Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1002@127.0.0.1", Username: "1002", Password: "CGRateS.org", Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1003@127.0.0.1", Username: "1003", Password: "CGRateS.org", Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1004@127.0.0.1", Username: "1004", Password: "CGRateS.org", Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1006@127.0.0.1", Username: "1006", Password: "CGRateS.org", Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1007@127.0.0.1", Username: "1007", Password: "CGRateS.org", Realm: "*", Registrar: "sip:127.0.0.1:5060"}}
if tutKamCallsPjSuaListener, err = engine.StartPjsuaListener(acnts, 5070, time.Duration(*waitRater)*time.Millisecond); err != nil {
t.Fatal(err)
}
}
// Call from 1001 (prepaid) to 1002
func TestTutKamCallsCall1001To1002(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "CGRateS.org", Realm: "*"}, "sip:1002@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(67)*time.Second, 5071); err != nil {
t.Fatal(err)
}
}
// Call from 1001 (prepaid) to 1003
func TestTutKamCallsCall1001To1003(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "CGRateS.org", Realm: "*"}, "sip:1003@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(65)*time.Second, 5072); err != nil {
t.Fatal(err)
}
}
func TestTutKamCallsCall1002To1001(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1002@127.0.0.1", Username: "1002", Password: "CGRateS.org", Realm: "*"}, "sip:1001@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(61)*time.Second, 5073); err != nil {
t.Fatal(err)
}
}
func TestTutKamCallsCall1003To1001(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1003@127.0.0.1", Username: "1003", Password: "CGRateS.org", Realm: "*"}, "sip:1001@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(63)*time.Second, 5074); err != nil {
t.Fatal(err)
}
}
func TestTutKamCallsCall1004To1001(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1004@127.0.0.1", Username: "1004", Password: "CGRateS.org", Realm: "*"}, "sip:1001@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(62)*time.Second, 5075); err != nil {
t.Fatal(err)
}
}
func TestTutKamCallsCall1006To1002(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1006@127.0.0.1", Username: "1006", Password: "CGRateS.org", Realm: "*"}, "sip:1002@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(64)*time.Second, 5076); err != nil {
t.Fatal(err)
}
}
func TestTutKamCallsCall1007To1002(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1007@127.0.0.1", Username: "1007", Password: "CGRateS.org", Realm: "*"}, "sip:1002@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(66)*time.Second, 5077); err != nil {
t.Fatal(err)
}
}
// Make sure account was debited properly
func TestTutKamCallsAccount1001(t *testing.T) {
time.Sleep(time.Duration(70) * time.Second) // Allow calls to finish before start querying the results
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutKamCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() == 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
} else if reply.Disabled == true {
t.Error("Account disabled")
}
}
// Make sure account was debited properly
func TestTutKamCalls1001Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
//var cgrId string // Share with getCostDetails
//var cCost engine.CallCost
req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, Accounts: []string{"1001"}, DestinationPrefixes: []string{"1002"}}
if err := tutKamCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
//cgrId = reply[0].CGRID
if reply[0].Source != "KAMAILIO_CGR_CALL_END" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Usage != "67" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
if reply[0].Cost == -1.0 { // Cost was not calculated
t.Errorf("Unexpected Cost for CDR: %+v", reply[0])
}
//if reply[0].Supplier != "suppl2" { // Usage as seconds
// t.Errorf("Unexpected Supplier for CDR: %+v", reply[0])
//}
}
/*
// Make sure call cost contains the matched information
if err := tutKamCallsRpc.Call("ApierV2.GetCallCostLog", utils.AttrGetCallCost{CgrId: cgrId}, &cCost); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if utils.IsSliceMember([]string{cCost.Timespans[0].MatchedSubject, cCost.Timespans[0].MatchedPrefix, cCost.Timespans[0].MatchedDestId}, "") {
t.Errorf("Unexpected Matched* for CallCost: %+v", cCost.Timespans[0])
}
*/
req = utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, Accounts: []string{"1001"}, DestinationPrefixes: []string{"1003"}}
if err := tutKamCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
//cgrId = reply[0].CGRID
if reply[0].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Usage != "65" && reply[0].Usage != "66" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
if reply[0].Cost != 0 { // Cost was not calculated
t.Errorf("Unexpected Cost for CDR: %+v", reply[0])
}
}
/*
// Make sure call cost contains the matched information
if err := tutKamCallsRpc.Call("ApierV2.GetCallCostLog", utils.AttrGetCallCost{CgrId: cgrId}, &cCost); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if utils.IsSliceMember([]string{cCost.Timespans[0].MatchedSubject, cCost.Timespans[0].MatchedPrefix, cCost.Timespans[0].MatchedDestId}, "") {
t.Errorf("Unexpected Matched* for CallCost: %+v", cCost.Timespans[0])
}
*/
req = utils.RPCCDRsFilter{Accounts: []string{"1001"}, RunIDs: []string{"derived_run1"}}
if err := tutKamCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 2 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].RequestType != utils.META_RATED {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Subject != "1002" {
t.Errorf("Unexpected Subject for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutKamCalls1002Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1002"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutKamCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 2 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "KAMAILIO_CGR_CALL_END" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_POSTPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "61" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutKamCalls1003Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1003"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutKamCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "KAMAILIO_CGR_CALL_END" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PSEUDOPREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "63" && reply[0].Usage != "64" { // Usage as seconds, sometimes takes a second longer to disconnect
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutKamCalls1004Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1004"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutKamCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "KAMAILIO_CGR_CALL_END" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_RATED {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "62" && reply[0].Usage != "63" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutKamCalls1006Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1006"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutKamCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 0 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
}
}
// Make sure account was debited properly
func TestTutKamCalls1007Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1007"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutKamCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "KAMAILIO_CGR_CALL_END" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1002" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "66" && reply[0].Usage != "67" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
if reply[0].Cost == -1.0 { // Cost was not calculated
t.Errorf("Unexpected Cost for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutKamCallsAccountFraud1001(t *testing.T) {
var reply string
attrAddBlnc := &v1.AttrAddBalance{Tenant: "cgrates.org", Account: "1001", BalanceType: "*monetary", Value: 101}
if err := tutKamCallsRpc.Call("ApierV1.AddBalance", attrAddBlnc, &reply); err != nil {
t.Error("Got error on ApierV1.AddBalance: ", err.Error())
} else if reply != "OK" {
t.Errorf("Calling ApierV1.AddBalance received: %s", reply)
}
}
// Based on Fraud automatic mitigation, our account should be disabled
func TestTutKamCallsAccountDisabled1001(t *testing.T) {
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutKamCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.Disabled == false {
t.Error("Account should be disabled per fraud detection rules.")
}
}
func TestTutKamCallsStopPjsuaListener(t *testing.T) {
tutKamCallsPjSuaListener.Write([]byte("q\n")) // Close pjsua
time.Sleep(time.Duration(1) * time.Second) // Allow pjsua to finish it's tasks, eg un-REGISTER
}
func TestTutKamCallsStopCgrEngine(t *testing.T) {
if err := engine.KillEngine(100); err != nil {
t.Error(err)
}
}
func TestTutKamCallsStopKam(t *testing.T) {
engine.KillProcName("kamailio", 1000)
}

View File

@@ -1,496 +0,0 @@
// +build calls
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package general_tests
import (
"net/rpc"
"net/rpc/jsonrpc"
"os"
"path"
"reflect"
"testing"
"time"
"github.com/cgrates/cgrates/apier/v1"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
var tutOsipsCallsCfg *config.CGRConfig
var tutOsipsCallsRpc *rpc.Client
var tutOsipsCallsPjSuaListener *os.File
func TestTutOsipsCallsInitCfg(t *testing.T) {
// Init config first
var err error
tutOsipsCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*dataDir, "tutorials", "osips_async", "cgrates", "etc", "cgrates"))
if err != nil {
t.Error(err)
}
tutOsipsCallsCfg.DataFolderPath = *dataDir // Share DataFolderPath through config towards StoreDb for Flush()
config.SetCgrConfig(tutOsipsCallsCfg)
}
// Remove data in both rating and accounting db
func TestTutOsipsCallsResetDataDb(t *testing.T) {
if err := engine.InitDataDb(tutOsipsCallsCfg); err != nil {
t.Fatal(err)
}
}
// Wipe out the cdr database
func TestTutOsipsCallsResetStorDb(t *testing.T) {
if err := engine.InitStorDb(tutOsipsCallsCfg); err != nil {
t.Fatal(err)
}
}
// start FS server
func TestTutOsipsCallsStartOsips(t *testing.T) {
engine.KillProcName("opensips", 3000)
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "osips_async", "opensips", "etc", "init.d", "opensips"), "start", 3000); err != nil {
t.Fatal(err)
}
}
// Start CGR Engine
func TestTutOsipsCallsStartEngine(t *testing.T) {
engine.KillProcName("cgr-engine", *waitRater)
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "osips_async", "cgrates", "etc", "init.d", "cgrates"), "start", 100); err != nil {
t.Fatal(err)
}
}
// Restart FS so we make sure reconnects are working
func TestTutOsipsCallsRestartOsips(t *testing.T) {
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "osips_async", "opensips", "etc", "init.d", "opensips"), "restart", 3000); err != nil {
t.Fatal(err)
}
}
// Connect rpc client to rater
func TestTutOsipsCallsRpcConn(t *testing.T) {
var err error
tutOsipsCallsRpc, err = jsonrpc.Dial("tcp", tutOsipsCallsCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
if err != nil {
t.Fatal(err)
}
}
// Load the tariff plan, creating accounts and their balances
func TestTutOsipsCallsLoadTariffPlanFromFolder(t *testing.T) {
reply := ""
attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial")}
if err := tutOsipsCallsRpc.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil {
t.Error(err)
} else if reply != "OK" {
t.Error(reply)
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups
}
// Make sure account was debited properly
func TestTutOsipsCallsAccountsBefore(t *testing.T) {
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutOsipsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1002"}
if err := tutOsipsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1003"}
if err := tutOsipsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1004"}
if err := tutOsipsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1007"}
if err := tutOsipsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 0.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1005"}
if err := tutOsipsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() {
t.Error("Got error on ApierV2.GetAccount: %v", err)
}
}
// Make sure all stats queues are in place
func TestTutOsipsCallsCdrStatsBefore(t *testing.T) {
//eQueueIds := []string{"*default", "CDRST1", "CDRST_1001", "CDRST_1002", "CDRST_1003", "STATS_SUPPL1", "STATS_SUPPL2"}
var statMetrics map[string]float64
eMetrics := map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutOsipsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST1"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
eMetrics = map[string]float64{engine.ACC: -1, engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1}
if err := tutOsipsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1001"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutOsipsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1002"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutOsipsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1003"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutOsipsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "STATS_SUPPL1"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutOsipsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "STATS_SUPPL2"}, &statMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(eMetrics, statMetrics) {
t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics)
}
}
// Start Pjsua as listener and register it to receive calls
func TestTutOsipsCallsStartPjsuaListener(t *testing.T) {
var err error
acnts := []*engine.PjsuaAccount{
&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1002@127.0.0.1", Username: "1002", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1003@127.0.0.1", Username: "1003", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1004@127.0.0.1", Username: "1004", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1006@127.0.0.1", Username: "1006", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1007@127.0.0.1", Username: "1007", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"}}
if tutOsipsCallsPjSuaListener, err = engine.StartPjsuaListener(acnts, 5070, time.Duration(*waitRater)*time.Millisecond); err != nil {
t.Fatal(err)
}
}
// Call from 1001 (prepaid) to 1002
func TestTutOsipsCallsCall1001To1002(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "1234", Realm: "*"}, "sip:1002@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(67)*time.Second, 5071); err != nil {
t.Fatal(err)
}
}
// Call from 1001 (prepaid) to 1003
func TestTutOsipsCallsCall1001To1003(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "1234", Realm: "*"}, "sip:1003@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(65)*time.Second, 5072); err != nil {
t.Fatal(err)
}
}
func TestTutOsipsCallsCall1002To1001(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1002@127.0.0.1", Username: "1002", Password: "1234", Realm: "*"}, "sip:1001@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(61)*time.Second, 5073); err != nil {
t.Fatal(err)
}
}
func TestTutOsipsCallsCall1003To1001(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1003@127.0.0.1", Username: "1003", Password: "1234", Realm: "*"}, "sip:1001@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(63)*time.Second, 5074); err != nil {
t.Fatal(err)
}
}
func TestTutOsipsCallsCall1004To1001(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1004@127.0.0.1", Username: "1004", Password: "1234", Realm: "*"}, "sip:1001@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(62)*time.Second, 5075); err != nil {
t.Fatal(err)
}
}
func TestTutOsipsCallsCall1006To1002(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1006@127.0.0.1", Username: "1006", Password: "1234", Realm: "*"}, "sip:1002@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(64)*time.Second, 5076); err != nil {
t.Fatal(err)
}
}
func TestTutOsipsCallsCall1007To1002(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1007@127.0.0.1", Username: "1007", Password: "1234", Realm: "*"}, "sip:1002@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(66)*time.Second, 5077); err != nil {
t.Fatal(err)
}
}
// Make sure account was debited properly
func TestTutOsipsCallsAccount1001(t *testing.T) {
time.Sleep(time.Duration(70) * time.Second) // Allow calls to finish before start querying the results
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutOsipsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() == 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
} else if reply.Disabled == true {
t.Error("Account disabled")
}
}
// Make sure account was debited properly
func TestTutOsipsCalls1001Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
//var cgrId string // Share with getCostDetails
//var cCost engine.CallCost
req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, Accounts: []string{"1001"}, DestinationPrefixes: []string{"1002"}}
if err := tutOsipsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
//cgrId = reply[0].CGRID
if reply[0].Source != "OSIPS_E_ACC_EVENT" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Usage != "67" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
if reply[0].Cost == -1.0 { // Cost was not calculated
t.Errorf("Unexpected Cost for CDR: %+v", reply[0])
}
//if reply[0].Supplier != "suppl2" { // Usage as seconds
// t.Errorf("Unexpected Supplier for CDR: %+v", reply[0])
//}
}
/*
// Make sure call cost contains the matched information
if err := tutOsipsCallsRpc.Call("ApierV2.GetCallCostLog", utils.AttrGetCallCost{CgrId: cgrId}, &cCost); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if utils.IsSliceMember([]string{cCost.Timespans[0].MatchedSubject, cCost.Timespans[0].MatchedPrefix, cCost.Timespans[0].MatchedDestId}, "") {
t.Errorf("Unexpected Matched* for CallCost: %+v", cCost.Timespans[0])
}
*/
req = utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, Accounts: []string{"1001"}, DestinationPrefixes: []string{"1003"}}
if err := tutOsipsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
//cgrId = reply[0].CGRID
if reply[0].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Usage != "65" && reply[0].Usage != "66" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
if reply[0].Cost != 0 { // Cost was not calculated
t.Errorf("Unexpected Cost for CDR: %+v", reply[0])
}
}
/*
// Make sure call cost contains the matched information
if err := tutOsipsCallsRpc.Call("ApierV2.GetCallCostLog", utils.AttrGetCallCost{CgrId: cgrId}, &cCost); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if utils.IsSliceMember([]string{cCost.Timespans[0].MatchedSubject, cCost.Timespans[0].MatchedPrefix, cCost.Timespans[0].MatchedDestId}, "") {
t.Errorf("Unexpected Matched* for CallCost: %+v", cCost.Timespans[0])
}
*/
req = utils.RPCCDRsFilter{Accounts: []string{"1001"}, RunIDs: []string{"derived_run1"}}
if err := tutOsipsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 2 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].RequestType != utils.META_RATED {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Subject != "1002" {
t.Errorf("Unexpected Subject for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutOsipsCalls1002Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1002"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutOsipsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 2 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "OSIPS_E_ACC_EVENT" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_POSTPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "61" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutOsipsCalls1003Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1003"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutOsipsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "OSIPS_E_ACC_EVENT" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PSEUDOPREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "63" && reply[0].Usage != "64" { // Usage as seconds, sometimes takes a second longer to disconnect
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutOsipsCalls1004Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1004"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutOsipsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "OSIPS_E_ACC_EVENT" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_RATED {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "62" && reply[0].Usage != "63" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
}
}
// Make sure aliasing was done for 1006 and we have no CDRs for it
func TestTutOsipsCalls1006Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1006"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutOsipsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 0 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
}
}
// Make sure account was debited properly
func TestTutOsipsCalls1007Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1007"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutOsipsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "OSIPS_E_ACC_EVENT" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1002" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "66" && reply[0].Usage != "67" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
if reply[0].Cost == -1.0 { // Cost was not calculated
t.Errorf("Unexpected Cost for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutOsipsCallsAccountFraud1001(t *testing.T) {
var reply string
attrAddBlnc := &v1.AttrAddBalance{Tenant: "cgrates.org", Account: "1001", BalanceType: "*monetary", Value: 101}
if err := tutOsipsCallsRpc.Call("ApierV1.AddBalance", attrAddBlnc, &reply); err != nil {
t.Error("Got error on ApierV1.AddBalance: ", err.Error())
} else if reply != "OK" {
t.Errorf("Calling ApierV1.AddBalance received: %s", reply)
}
}
// Based on Fraud automatic mitigation, our account should be disabled
func TestTutOsipsCallsAccountDisabled1001(t *testing.T) {
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutOsipsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.Disabled == false {
t.Error("Account should be disabled per fraud detection rules.")
}
}
func TestTutOsipsCallsStopPjsuaListener(t *testing.T) {
tutOsipsCallsPjSuaListener.Write([]byte("q\n")) // Close pjsua
time.Sleep(time.Duration(1) * time.Second) // Allow pjsua to finish it's tasks, eg un-REGISTER
}
func TestTutOsipsCallsStopCgrEngine(t *testing.T) {
if err := engine.KillEngine(100); err != nil {
t.Error(err)
}
}
func TestTutOsipsCallsStopOpensips(t *testing.T) {
engine.KillProcName("opensips", 100)
}

View File

@@ -102,6 +102,42 @@ func TestAccountITMove(t *testing.T) {
}
}
func TestAccountITMoveEncoding(t *testing.T) {
var err error
accPathIn = path.Join(*dataDir, "conf", "samples", "tutmongojson")
accCfgIn, err = config.NewCGRConfigFromFolder(accPathIn)
if err != nil {
t.Fatal(err)
}
accPathOut = path.Join(*dataDir, "conf", "samples", "tutmongomsgpack")
accCfgOut, err = config.NewCGRConfigFromFolder(accPathOut)
if err != nil {
t.Fatal(err)
}
accAction = utils.Move
for _, stest := range sTestsAccIT {
t.Run("TestAccountITMove", stest)
}
}
func TestAccountITMoveEncoding2(t *testing.T) {
var err error
accPathIn = path.Join(*dataDir, "conf", "samples", "tutmysqljson")
accCfgIn, err = config.NewCGRConfigFromFolder(accPathIn)
if err != nil {
t.Fatal(err)
}
accPathOut = path.Join(*dataDir, "conf", "samples", "tutmysqlmsgpack")
accCfgOut, err = config.NewCGRConfigFromFolder(accPathOut)
if err != nil {
t.Fatal(err)
}
accAction = utils.Move
for _, stest := range sTestsAccIT {
t.Run("TestAccountITMove", stest)
}
}
func testAccITConnect(t *testing.T) {
dataDBIn, err := NewMigratorDataDB(accCfgIn.DataDbType,
accCfgIn.DataDbHost, accCfgIn.DataDbPort, accCfgIn.DataDbName,

249
migrator/action_it_test.go Normal file
View File

@@ -0,0 +1,249 @@
// +build integration
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package migrator
import (
"log"
"path"
"reflect"
"testing"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
var (
actPathIn string
actPathOut string
actCfgIn *config.CGRConfig
actCfgOut *config.CGRConfig
actMigrator *Migrator
actAction string
)
var sTestsActIT = []func(t *testing.T){
testActITConnect,
testActITFlush,
testActITMigrateAndMove,
}
func TestActionITRedis(t *testing.T) {
var err error
actPathIn = path.Join(*dataDir, "conf", "samples", "tutmysql")
actCfgIn, err = config.NewCGRConfigFromFolder(actPathIn)
if err != nil {
t.Fatal(err)
}
actCfgOut, err = config.NewCGRConfigFromFolder(actPathIn)
if err != nil {
t.Fatal(err)
}
actAction = utils.Migrate
for _, stest := range sTestsActIT {
t.Run("TestActionITMigrateRedis", stest)
}
}
func TestActionITMongo(t *testing.T) {
var err error
actPathIn = path.Join(*dataDir, "conf", "samples", "tutmongo")
actCfgIn, err = config.NewCGRConfigFromFolder(actPathIn)
if err != nil {
t.Fatal(err)
}
actCfgOut, err = config.NewCGRConfigFromFolder(actPathIn)
if err != nil {
t.Fatal(err)
}
actAction = utils.Migrate
for _, stest := range sTestsActIT {
t.Run("TestActionITMigrateMongo", stest)
}
}
func TestActionITMove(t *testing.T) {
var err error
actPathIn = path.Join(*dataDir, "conf", "samples", "tutmongo")
actCfgIn, err = config.NewCGRConfigFromFolder(actPathIn)
if err != nil {
t.Fatal(err)
}
actPathOut = path.Join(*dataDir, "conf", "samples", "tutmysql")
actCfgOut, err = config.NewCGRConfigFromFolder(actPathOut)
if err != nil {
t.Fatal(err)
}
actAction = utils.Move
for _, stest := range sTestsActIT {
t.Run("TestActionITMove", stest)
}
}
func TestActionITMoveEncoding(t *testing.T) {
var err error
actPathIn = path.Join(*dataDir, "conf", "samples", "tutmongojson")
actCfgIn, err = config.NewCGRConfigFromFolder(actPathIn)
if err != nil {
t.Fatal(err)
}
actPathOut = path.Join(*dataDir, "conf", "samples", "tutmongomsgpack")
actCfgOut, err = config.NewCGRConfigFromFolder(actPathOut)
if err != nil {
t.Fatal(err)
}
actAction = utils.Move
for _, stest := range sTestsActIT {
t.Run("TestActionITMoveEncoding", stest)
}
}
/*
func TestActionITMoveEncoding2(t *testing.T) {
var err error
actPathIn = path.Join(*dataDir, "conf", "samples", "tutmysqljson")
actCfgIn, err = config.NewCGRConfigFromFolder(actPathIn)
if err != nil {
t.Fatal(err)
}
actPathOut = path.Join(*dataDir, "conf", "samples", "tutmysqlmsgpack")
actCfgOut, err = config.NewCGRConfigFromFolder(actPathOut)
if err != nil {
t.Fatal(err)
}
actAction = utils.Move
for _, stest := range sTestsActIT {
t.Run("TestActionITMoveEncoding2", stest)
}
}*/
func testActITConnect(t *testing.T) {
dataDBIn, err := NewMigratorDataDB(actCfgIn.DataDbType,
actCfgIn.DataDbHost, actCfgIn.DataDbPort, actCfgIn.DataDbName,
actCfgIn.DataDbUser, actCfgIn.DataDbPass, actCfgIn.DBDataEncoding,
config.CgrConfig().CacheCfg(), *loadHistorySize)
if err != nil {
log.Fatal(err)
}
dataDBOut, err := NewMigratorDataDB(actCfgOut.DataDbType,
actCfgOut.DataDbHost, actCfgOut.DataDbPort, actCfgOut.DataDbName,
actCfgOut.DataDbUser, actCfgOut.DataDbPass, actCfgOut.DBDataEncoding,
config.CgrConfig().CacheCfg(), *loadHistorySize)
if err != nil {
log.Fatal(err)
}
actMigrator, err = NewMigrator(dataDBIn, dataDBOut,
nil, nil,
false, false, false)
if err != nil {
log.Fatal(err)
}
}
func testActITFlush(t *testing.T) {
actMigrator.dmOut.DataManager().DataDB().Flush("")
if err := engine.SetDBVersions(actMigrator.dmOut.DataManager().DataDB()); err != nil {
t.Error("Error ", err.Error())
}
}
func testActITMigrateAndMove(t *testing.T) {
timingSlice := []*engine.RITiming{
&engine.RITiming{
Years: utils.Years{},
Months: utils.Months{},
MonthDays: utils.MonthDays{},
WeekDays: utils.WeekDays{},
},
}
v1act := &v1Action{
Id: "test",
ActionType: "",
BalanceType: "",
Direction: "INBOUND",
ExtraParameters: "",
ExpirationString: "",
Balance: &v1Balance{
Timings: timingSlice,
},
}
v1acts := &v1Actions{
v1act,
}
act := &engine.Actions{
&engine.Action{
Id: "test",
ActionType: "",
ExtraParameters: "",
ExpirationString: "",
Weight: 0.00,
Balance: &engine.BalanceFilter{
Timings: timingSlice,
},
},
}
switch actAction {
case utils.Migrate:
err := actMigrator.dmIN.setV1Actions(v1acts)
if err != nil {
t.Error("Error when setting v1 Actions ", err.Error())
}
currentVersion := engine.Versions{utils.StatS: 2, utils.Thresholds: 2, utils.Accounts: 2, utils.Actions: 1, utils.ActionTriggers: 2, utils.ActionPlans: 2, utils.SharedGroups: 2}
err = actMigrator.dmOut.DataManager().DataDB().SetVersions(currentVersion, false)
if err != nil {
t.Error("Error when setting version for Actions ", err.Error())
}
err, _ = actMigrator.Migrate([]string{utils.MetaActions})
if err != nil {
t.Error("Error when migrating Actions ", err.Error())
}
result, err := actMigrator.dmOut.DataManager().GetActions(v1act.Id, true, utils.NonTransactional)
if err != nil {
t.Error("Error when getting Actions ", err.Error())
}
if !reflect.DeepEqual(act, &result) {
t.Errorf("Expecting: %+v, received: %+v", act, &result)
}
case utils.Move:
if err := actMigrator.dmIN.DataManager().SetActions(v1act.Id, *act, utils.NonTransactional); err != nil {
t.Error("Error when setting ActionPlan ", err.Error())
}
currentVersion := engine.CurrentDataDBVersions()
err := actMigrator.dmOut.DataManager().DataDB().SetVersions(currentVersion, false)
if err != nil {
t.Error("Error when setting version for Actions ", err.Error())
}
err, _ = actMigrator.Migrate([]string{utils.MetaActions})
if err != nil {
t.Error("Error when migrating Actions ", err.Error())
}
result, err := actMigrator.dmOut.DataManager().GetActions(v1act.Id, true, utils.NonTransactional)
if err != nil {
t.Error("Error when getting Actions ", err.Error())
}
if !reflect.DeepEqual(act, &result) {
t.Errorf("Expecting: %+v, received: %+v", act, &result)
}
}
}

View File

@@ -0,0 +1,250 @@
// +build integration
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package migrator
import (
"log"
"path"
"reflect"
"testing"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
var (
actPlnPathIn string
actPlnPathOut string
actPlnCfgIn *config.CGRConfig
actPlnCfgOut *config.CGRConfig
actPlnMigrator *Migrator
actActionPlan string
)
var sTestsActPlnIT = []func(t *testing.T){
testActPlnITConnect,
testActPlnITFlush,
testActPlnITMigrateAndMove,
}
func TestActionPlanITRedis(t *testing.T) {
var err error
actPlnPathIn = path.Join(*dataDir, "conf", "samples", "tutmysql")
actPlnCfgIn, err = config.NewCGRConfigFromFolder(actPlnPathIn)
if err != nil {
t.Fatal(err)
}
actPlnCfgOut, err = config.NewCGRConfigFromFolder(actPlnPathIn)
if err != nil {
t.Fatal(err)
}
actActionPlan = utils.Migrate
for _, stest := range sTestsActPlnIT {
t.Run("TestActionPlanITMigrateRedis", stest)
}
}
func TestActionPlanITMongo(t *testing.T) {
var err error
actPlnPathIn = path.Join(*dataDir, "conf", "samples", "tutmongo")
actPlnCfgIn, err = config.NewCGRConfigFromFolder(actPlnPathIn)
if err != nil {
t.Fatal(err)
}
actPlnCfgOut, err = config.NewCGRConfigFromFolder(actPlnPathIn)
if err != nil {
t.Fatal(err)
}
actActionPlan = utils.Migrate
for _, stest := range sTestsActPlnIT {
t.Run("TestActionPlanITMigrateMongo", stest)
}
}
func TestActionPlanITMove(t *testing.T) {
var err error
actPlnPathIn = path.Join(*dataDir, "conf", "samples", "tutmongo")
actPlnCfgIn, err = config.NewCGRConfigFromFolder(actPlnPathIn)
if err != nil {
t.Fatal(err)
}
actPlnPathOut = path.Join(*dataDir, "conf", "samples", "tutmysql")
actPlnCfgOut, err = config.NewCGRConfigFromFolder(actPlnPathOut)
if err != nil {
t.Fatal(err)
}
actActionPlan = utils.Move
for _, stest := range sTestsActPlnIT {
t.Run("TestActionPlanITMove", stest)
}
}
func TestActionPlanITMoveEncoding(t *testing.T) {
var err error
actPlnPathIn = path.Join(*dataDir, "conf", "samples", "tutmongojson")
actPlnCfgIn, err = config.NewCGRConfigFromFolder(actPlnPathIn)
if err != nil {
t.Fatal(err)
}
actPlnPathOut = path.Join(*dataDir, "conf", "samples", "tutmongomsgpack")
actPlnCfgOut, err = config.NewCGRConfigFromFolder(actPlnPathOut)
if err != nil {
t.Fatal(err)
}
actActionPlan = utils.Move
for _, stest := range sTestsActPlnIT {
t.Run("TestActionPlanITMoveEncoding", stest)
}
}
func TestActionPlanITMoveEncoding2(t *testing.T) {
var err error
actPlnPathIn = path.Join(*dataDir, "conf", "samples", "tutmysqljson")
actPlnCfgIn, err = config.NewCGRConfigFromFolder(actPlnPathIn)
if err != nil {
t.Fatal(err)
}
actPlnPathOut = path.Join(*dataDir, "conf", "samples", "tutmysqlmsgpack")
actPlnCfgOut, err = config.NewCGRConfigFromFolder(actPlnPathOut)
if err != nil {
t.Fatal(err)
}
actActionPlan = utils.Move
for _, stest := range sTestsActPlnIT {
t.Run("TestActionPlanITMoveEncoding2", stest)
}
}
func testActPlnITConnect(t *testing.T) {
dataDBIn, err := NewMigratorDataDB(actPlnCfgIn.DataDbType,
actPlnCfgIn.DataDbHost, actPlnCfgIn.DataDbPort, actPlnCfgIn.DataDbName,
actPlnCfgIn.DataDbUser, actPlnCfgIn.DataDbPass, actPlnCfgIn.DBDataEncoding,
config.CgrConfig().CacheCfg(), *loadHistorySize)
if err != nil {
log.Fatal(err)
}
dataDBOut, err := NewMigratorDataDB(actPlnCfgOut.DataDbType,
actPlnCfgOut.DataDbHost, actPlnCfgOut.DataDbPort, actPlnCfgOut.DataDbName,
actPlnCfgOut.DataDbUser, actPlnCfgOut.DataDbPass, actPlnCfgOut.DBDataEncoding,
config.CgrConfig().CacheCfg(), *loadHistorySize)
if err != nil {
log.Fatal(err)
}
actPlnMigrator, err = NewMigrator(dataDBIn, dataDBOut,
nil, nil,
false, false, false)
if err != nil {
log.Fatal(err)
}
}
func testActPlnITFlush(t *testing.T) {
actPlnMigrator.dmOut.DataManager().DataDB().Flush("")
if err := engine.SetDBVersions(actPlnMigrator.dmOut.DataManager().DataDB()); err != nil {
t.Error("Error ", err.Error())
}
}
func testActPlnITMigrateAndMove(t *testing.T) {
timingSlice := &engine.RITiming{
Years: utils.Years{},
Months: utils.Months{},
MonthDays: utils.MonthDays{},
WeekDays: utils.WeekDays{},
}
v1actPln := &v1ActionPlans{
&v1ActionPlan{
Id: "test",
AccountIds: []string{"one"},
Timing: &engine.RateInterval{
Timing: timingSlice,
},
},
}
actPln := &engine.ActionPlan{
Id: "test",
AccountIDs: utils.StringMap{"one": true},
ActionTimings: []*engine.ActionTiming{
&engine.ActionTiming{
Timing: &engine.RateInterval{
Timing: timingSlice,
},
},
},
}
switch actActionPlan {
case utils.Migrate:
err := actPlnMigrator.dmIN.setV1ActionPlans(v1actPln)
if err != nil {
t.Error("Error when setting v1 ActionPlan ", err.Error())
}
currentVersion := engine.Versions{utils.StatS: 2, utils.Thresholds: 2, utils.Accounts: 2, utils.Actions: 2, utils.ActionTriggers: 2, utils.ActionPlans: 1, utils.SharedGroups: 2}
err = actPlnMigrator.dmOut.DataManager().DataDB().SetVersions(currentVersion, false)
if err != nil {
t.Error("Error when setting version for ActionPlan ", err.Error())
}
err, _ = actPlnMigrator.Migrate([]string{utils.MetaActionPlans})
if err != nil {
t.Error("Error when migrating ActionPlan ", err.Error())
}
result, err := actPlnMigrator.dmOut.DataManager().DataDB().GetActionPlan((*v1actPln)[0].Id, true, utils.NonTransactional)
if err != nil {
t.Error("Error when getting ActionPlan ", err.Error())
}
// compared fields, uuid is generated in ActionTiming
if !reflect.DeepEqual(actPln.Id, result.Id) {
t.Errorf("Expecting: %+v, received: %+v", actPln.Id, result.Id)
} else if !reflect.DeepEqual(actPln.AccountIDs, result.AccountIDs) {
t.Errorf("Expecting: %+v, received: %+v", actPln.AccountIDs, result.AccountIDs)
} else if !reflect.DeepEqual(actPln.ActionTimings[0].Timing, result.ActionTimings[0].Timing) {
t.Errorf("Expecting: %+v, received: %+v", actPln.ActionTimings[0].Timing, result.ActionTimings[0].Timing)
}
case utils.Move:
if err := actPlnMigrator.dmIN.DataManager().DataDB().SetActionPlan((*v1actPln)[0].Id, actPln, true, utils.NonTransactional); err != nil {
t.Error("Error when setting ActionPlan ", err.Error())
}
currentVersion := engine.CurrentDataDBVersions()
err := actPlnMigrator.dmOut.DataManager().DataDB().SetVersions(currentVersion, false)
if err != nil {
t.Error("Error when setting version for ActionPlan ", err.Error())
}
err, _ = actPlnMigrator.Migrate([]string{utils.MetaActionPlans})
if err != nil {
t.Error("Error when migrating ActionPlan ", err.Error())
}
result, err := actPlnMigrator.dmOut.DataManager().DataDB().GetActionPlan((*v1actPln)[0].Id, true, utils.NonTransactional)
if err != nil {
t.Error("Error when getting ActionPlan ", err.Error())
}
// compared fields, uuid is generated in ActionTiming
if !reflect.DeepEqual(actPln.Id, result.Id) {
t.Errorf("Expecting: %+v, received: %+v", actPln.Id, result.Id)
} else if !reflect.DeepEqual(actPln.AccountIDs, result.AccountIDs) {
t.Errorf("Expecting: %+v, received: %+v", actPln.AccountIDs, result.AccountIDs)
} else if !reflect.DeepEqual(actPln.ActionTimings[0].Timing, result.ActionTimings[0].Timing) {
t.Errorf("Expecting: %+v, received: %+v", actPln.ActionTimings[0].Timing, result.ActionTimings[0].Timing)
}
}
}

View File

@@ -0,0 +1,248 @@
// +build integration
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package migrator
/*
import (
//"flag"
"log"
"path"
"reflect"
"testing"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
var (
actTrgPathIn string
actTrgPathOut string
actTrgCfgIn *config.CGRConfig
actTrgCfgOut *config.CGRConfig
actTrgMigrator *Migrator
actActionTrigger string
)
var sTestsActTrgIT = []func(t *testing.T){
testActTrgITConnect,
testActTrgITFlush,
testActTrgITMigrateAndMove,
}
func TestActionTriggerITRedis(t *testing.T) {
var err error
actTrgPathIn = path.Join(*dataDir, "conf", "samples", "tutmysql")
actTrgCfgIn, err = config.NewCGRConfigFromFolder(actTrgPathIn)
if err != nil {
t.Fatal(err)
}
actTrgCfgOut, err = config.NewCGRConfigFromFolder(actTrgPathIn)
if err != nil {
t.Fatal(err)
}
actActionTrigger = utils.Migrate
for _, stest := range sTestsActTrgIT {
t.Run("TestActionTriggerITMigrateRedis", stest)
}
}
func TestActionTriggerITMongo(t *testing.T) {
var err error
actTrgPathIn = path.Join(*dataDir, "conf", "samples", "tutmongo")
actTrgCfgIn, err = config.NewCGRConfigFromFolder(actTrgPathIn)
if err != nil {
t.Fatal(err)
}
actTrgCfgOut, err = config.NewCGRConfigFromFolder(actTrgPathIn)
if err != nil {
t.Fatal(err)
}
actActionTrigger = utils.Migrate
for _, stest := range sTestsActTrgIT {
t.Run("TestActionTriggerITMigrateMongo", stest)
}
}
func TestActionTriggerITMove(t *testing.T) {
var err error
actTrgPathIn = path.Join(*dataDir, "conf", "samples", "tutmongo")
actTrgCfgIn, err = config.NewCGRConfigFromFolder(actTrgPathIn)
if err != nil {
t.Fatal(err)
}
actTrgPathOut = path.Join(*dataDir, "conf", "samples", "tutmysql")
actTrgCfgOut, err = config.NewCGRConfigFromFolder(actTrgPathOut)
if err != nil {
t.Fatal(err)
}
actActionTrigger = utils.Move
for _, stest := range sTestsActTrgIT {
t.Run("TestActionTriggerITMove", stest)
}
}
func TestActionTriggerITMoveEncoding(t *testing.T) {
var err error
actTrgPathIn = path.Join(*dataDir, "conf", "samples", "tutmongojson")
actTrgCfgIn, err = config.NewCGRConfigFromFolder(actTrgPathIn)
if err != nil {
t.Fatal(err)
}
actTrgPathOut = path.Join(*dataDir, "conf", "samples", "tutmongomsgpack")
actTrgCfgOut, err = config.NewCGRConfigFromFolder(actTrgPathOut)
if err != nil {
t.Fatal(err)
}
actActionTrigger = utils.Move
for _, stest := range sTestsActTrgIT {
t.Run("TestActionTriggerITMoveEncoding", stest)
}
}
func TestActionTriggerITMoveEncoding2(t *testing.T) {
var err error
actTrgPathIn = path.Join(*dataDir, "conf", "samples", "tutmysqljson")
actTrgCfgIn, err = config.NewCGRConfigFromFolder(actTrgPathIn)
if err != nil {
t.Fatal(err)
}
actTrgPathOut = path.Join(*dataDir, "conf", "samples", "tutmysqlmsgpack")
actTrgCfgOut, err = config.NewCGRConfigFromFolder(actTrgPathOut)
if err != nil {
t.Fatal(err)
}
actActionTrigger = utils.Move
for _, stest := range sTestsActTrgIT {
t.Run("TestActionTriggerITMoveEncoding2", stest)
}
}
func testActTrgITConnect(t *testing.T) {
dataDBIn, err := NewMigratorDataDB(actTrgCfgIn.DataDbType,
actTrgCfgIn.DataDbHost, actTrgCfgIn.DataDbPort, actTrgCfgIn.DataDbName,
actTrgCfgIn.DataDbUser, actTrgCfgIn.DataDbPass, actTrgCfgIn.DBDataEncoding,
config.CgrConfig().CacheCfg(), *loadHistorySize)
if err != nil {
log.Fatal(err)
}
dataDBOut, err := NewMigratorDataDB(actTrgCfgOut.DataDbType,
actTrgCfgOut.DataDbHost, actTrgCfgOut.DataDbPort, actTrgCfgOut.DataDbName,
actTrgCfgOut.DataDbUser, actTrgCfgOut.DataDbPass, actTrgCfgOut.DBDataEncoding,
config.CgrConfig().CacheCfg(), *loadHistorySize)
if err != nil {
log.Fatal(err)
}
actTrgMigrator, err = NewMigrator(dataDBIn, dataDBOut,
nil, nil,
false, false, false)
if err != nil {
log.Fatal(err)
}
}
func testActTrgITFlush(t *testing.T) {
actTrgMigrator.dmOut.DataManager().DataDB().Flush("")
if err := engine.SetDBVersions(actTrgMigrator.dmOut.DataManager().DataDB()); err != nil {
t.Error("Error ", err.Error())
}
}
func testActTrgITMigrateAndMove(t *testing.T) {
tim := time.Date(2012, time.February, 27, 23, 59, 59, 0, time.UTC)
v1actTrg := &v1ActionTriggers{
&v1ActionTrigger{
Id: "Test",
BalanceType: "*monetary",
BalanceDirection: "*out",
ThresholdType: "*max_balance",
ThresholdValue: 2,
ActionsId: "TEST_ACTIONS",
Executed: true,
BalanceExpirationDate: tim,
},
}
actTrg := engine.ActionTriggers{
&engine.ActionTrigger{
ID: "Test",
Balance: &engine.BalanceFilter{
Timings: []*engine.RITiming{},
ExpirationDate: utils.TimePointer(tim),
Type: utils.StringPointer(utils.MONETARY),
Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)),
},
ExpirationDate: tim,
LastExecutionTime: tim,
ActivationDate: tim,
ThresholdType: utils.TRIGGER_MAX_BALANCE,
ThresholdValue: 2,
ActionsID: "TEST_ACTIONS",
Executed: true,
},
}
switch actActionTrigger {
case utils.Migrate:
err := actTrgMigrator.dmIN.setV2ActionTrigger(v1actTrg)
if err != nil {
t.Error("Error when setting v1 ActionTriggers ", err.Error())
}
currentVersion := engine.Versions{utils.StatS: 2, utils.Thresholds: 2, utils.Accounts: 2, utils.Actions: 2, utils.ActionTriggers: 1, utils.ActionPlans: 2, utils.SharedGroups: 2}
err = actTrgMigrator.dmOut.DataManager().DataDB().SetVersions(currentVersion, false)
if err != nil {
t.Error("Error when setting version for ActionTriggers ", err.Error())
}
err, _ = actTrgMigrator.Migrate([]string{utils.MetaActionTriggers})
if err != nil {
t.Error("Error when migrating ActionTriggers ", err.Error())
}
result, err := actTrgMigrator.dmOut.DataManager().GetActionTriggers((*v1actTrg)[0].Id, false, utils.NonTransactional)
if err != nil {
t.Error("Error when getting ActionTriggers ", err.Error())
}
if !reflect.DeepEqual(actTrg, result) {
t.Errorf("Expecting: %+v, received: %+v", actTrg, result)
}
// utils.tojson si verificat
case utils.Move:
if err := actTrgMigrator.dmIN.DataManager().SetActionTriggers((*v1actTrg)[0].Id, actTrg, utils.NonTransactional); err != nil {
t.Error("Error when setting ActionTriggers ", err.Error())
}
currentVersion := engine.CurrentDataDBVersions()
err := actTrgMigrator.dmOut.DataManager().DataDB().SetVersions(currentVersion, false)
if err != nil {
t.Error("Error when setting version for ActionTriggers ", err.Error())
}
err, _ = actTrgMigrator.Migrate([]string{utils.MetaActionTriggers})
if err != nil {
t.Error("Error when migrating ActionTriggers ", err.Error())
}
result, err := actTrgMigrator.dmOut.DataManager().GetActionTriggers((*v1actTrg)[0].Id, false, utils.NonTransactional)
if err != nil {
t.Error("Error when getting ActionTriggers ", err.Error())
}
if !reflect.DeepEqual(actTrg, result) {
t.Errorf("Expecting: %+v, received: %+v", actTrg, result)
}
}
}
*/

File diff suppressed because it is too large Load Diff

View File

@@ -25,6 +25,8 @@ import (
type MigratorStorDB interface {
getV1CDR() (v1Cdr *v1Cdrs, err error)
setV1CDR(v1Cdr *v1Cdrs) (err error)
createV1SMCosts() (err error)
renameV1SMCosts() (err error)
getV2SMCost() (v2Cost *v2SessionsCost, err error)
setV2SMCost(v2Cost *v2SessionsCost) (err error)
remV2SMCost(v2Cost *v2SessionsCost) (err error)

View File

@@ -64,31 +64,12 @@ func NewMigratorStorDB(db_type, host, port, name, user, pass string,
case utils.MYSQL:
d = newMigratorSQL(storDb)
db = d.(MigratorStorDB)
case utils.POSTGRES:
d = newMigratorSQL(storDb)
db = d.(MigratorStorDB)
default:
err = errors.New(fmt.Sprintf("Unknown db '%s' valid options are '%s' or '%s'",
db_type, utils.MONGO, utils.MYSQL))
}
return d, nil
}
/*
func ConfigureV1StorDB(db_type, host, port, name, user, pass string) (db MigratorStorDB, err error) {
var d MigratorStorDB
switch db_type {
case utils.MONGO:
d, err = newv1MongoStorage(host, port, name, user, pass, utils.StorDB, nil)
db = d.(MigratorStorDB)
case utils.MYSQL:
d, err = newSqlStorage(host, port, name, user, pass)
db = d.(MigratorStorDB)
default:
err = errors.New(fmt.Sprintf("Unknown db '%s' valid options are '%s'",
db_type, utils.MONGO))
}
if err != nil {
return nil, err
}
return d, nil
}
*/

View File

@@ -19,7 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package migrator
import (
//"database/sql"
"encoding/json"
"fmt"
"time"
@@ -63,30 +62,10 @@ func (m *Migrator) migrateSessionSCosts() (err error) {
"version number is not defined for SessionsCosts model")
}
switch vrs[utils.SessionSCosts] {
// case 0, 1:
// var isPostGres bool
// var storSQL *sql.DB
// switch m.storDBType {
// case utils.MYSQL:
// isPostGres = false
// storSQL = m.storDBOut.(*engine.SQLStorage).Db
// case utils.POSTGRES:
// isPostGres = true
// storSQL = m.storDBOut.(*engine.SQLStorage).Db
// default:
// return utils.NewCGRError(utils.Migrator,
// utils.MandatoryIEMissingCaps,
// utils.UnsupportedDB,
// fmt.Sprintf("unsupported database type: <%s>", m.storDBType))
// }
// qry := "RENAME TABLE sm_costs TO sessions_costs;"
// if isPostGres {
// qry = "ALTER TABLE sm_costs RENAME TO sessions_costs"
// }
// if _, err := storSQL.Exec(qry); err != nil {
// return err
// }
// fallthrough // incremental updates
case 0, 1:
if err := m.migrateV1SessionSCosts(); err != nil {
return err
}
case 2:
if err := m.migrateV2SessionSCosts(); err != nil {
return err
@@ -99,6 +78,22 @@ func (m *Migrator) migrateSessionSCosts() (err error) {
return nil
}
func (m *Migrator) migrateV1SessionSCosts() (err error) {
if err = m.storDBIn.renameV1SMCosts(); err != nil {
return err
}
if m.dryRun != true {
vrs := engine.Versions{utils.SessionSCosts: 2}
if err = m.storDBOut.StorDB().SetVersions(vrs, false); err != nil {
return utils.NewCGRError(utils.Migrator,
utils.ServerErrorCaps,
err.Error(),
fmt.Sprintf("error: <%s> when updating SessionSCosts version into StorDB", err.Error()))
}
}
return
}
func (m *Migrator) migrateV2SessionSCosts() (err error) {
var v2Cost *v2SessionsCost
for {

View File

@@ -20,9 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package migrator
/*
import (
"log"
"path"
"testing"
"time"
@@ -42,67 +40,46 @@ var (
)
var sTestssCostIT = []func(t *testing.T){
testSessionCostITConnect,
testSessionCostITRename,
testSessionCostITFlush,
testSessionCostITMigrateAndMove,
testSessionCostITMigrate,
}
func TestSessionCostITMongoConnection(t *testing.T) {
func TestSessionCostITMongo(t *testing.T) {
var err error
sCostPathIn = path.Join(*dataDir, "conf", "samples", "tutmongo")
sCostCfgIn, err = config.NewCGRConfigFromFolder(sCostPathIn)
if err != nil {
t.Error(err)
}
storDBIn, err := engine.ConfigureStorDB(sCostCfgIn.StorDBType, sCostCfgIn.StorDBHost,
sCostCfgIn.StorDBPort, sCostCfgIn.StorDBName,
sCostCfgIn.StorDBUser, sCostCfgIn.StorDBPass,
config.CgrConfig().StorDBMaxOpenConns,
config.CgrConfig().StorDBMaxIdleConns,
config.CgrConfig().StorDBConnMaxLifetime,
config.CgrConfig().StorDBCDRSIndexes)
sCostCfgOut, err = config.NewCGRConfigFromFolder(sCostPathIn)
if err != nil {
t.Error(err)
}
storDBOut, err := engine.ConfigureStorDB(sCostCfgIn.StorDBType,
sCostCfgIn.StorDBHost, sCostCfgIn.StorDBPort, sCostCfgIn.StorDBName,
sCostCfgIn.StorDBUser, sCostCfgIn.StorDBPass,
config.CgrConfig().StorDBMaxOpenConns,
config.CgrConfig().StorDBMaxIdleConns,
config.CgrConfig().StorDBConnMaxLifetime,
config.CgrConfig().StorDBCDRSIndexes)
if err != nil {
t.Error(err)
}
oldStorDB, err := ConfigureV1StorDB(sCostCfgIn.StorDBType,
sCostCfgIn.StorDBHost, sCostCfgIn.StorDBPort, sCostCfgIn.StorDBName,
sCostCfgIn.StorDBUser, sCostCfgIn.StorDBPass)
if err != nil {
log.Fatal(err)
}
sCostMigrator, err = NewMigrator(nil, nil, sCostCfgIn.DataDbType,
sCostCfgIn.DBDataEncoding, storDBIn, storDBOut, sCostCfgIn.StorDBType, nil,
sCostCfgIn.DataDbType, sCostCfgIn.DBDataEncoding, oldStorDB, sCostCfgIn.StorDBType,
false, false, false, false, false)
if err != nil {
t.Error(err)
}
}
func TestSessionCostITMongo(t *testing.T) {
for _, stest := range sTestssCostIT {
t.Run("TestSessionSCostITMigrateMongo", stest)
}
}
func TestSessionCostITMySqlConnection(t *testing.T) {
func TestSessionCostITMySql(t *testing.T) {
var err error
sCostPathIn = path.Join(*dataDir, "conf", "samples", "tutmysql")
sCostCfgIn, err = config.NewCGRConfigFromFolder(sCostPathIn)
if err != nil {
t.Error(err)
}
storDBIn, err := engine.ConfigureStorDB(sCostCfgIn.StorDBType, sCostCfgIn.StorDBHost,
sCostCfgOut, err = config.NewCGRConfigFromFolder(sCostPathIn)
if err != nil {
t.Error(err)
}
for _, stest := range sTestssCostIT {
t.Run("TestSessionSCostITMigrateMySql", stest)
}
}
func testSessionCostITConnect(t *testing.T) {
storDBIn, err := NewMigratorStorDB(sCostCfgIn.StorDBType, sCostCfgIn.StorDBHost,
sCostCfgIn.StorDBPort, sCostCfgIn.StorDBName,
sCostCfgIn.StorDBUser, sCostCfgIn.StorDBPass,
config.CgrConfig().StorDBMaxOpenConns,
@@ -112,9 +89,9 @@ func TestSessionCostITMySqlConnection(t *testing.T) {
if err != nil {
t.Error(err)
}
storDBOut, err := engine.ConfigureStorDB(sCostCfgIn.StorDBType,
sCostCfgIn.StorDBHost, sCostCfgIn.StorDBPort, sCostCfgIn.StorDBName,
sCostCfgIn.StorDBUser, sCostCfgIn.StorDBPass,
storDBOut, err := NewMigratorStorDB(sCostCfgOut.StorDBType,
sCostCfgOut.StorDBHost, sCostCfgOut.StorDBPort, sCostCfgOut.StorDBName,
sCostCfgOut.StorDBUser, sCostCfgOut.StorDBPass,
config.CgrConfig().StorDBMaxOpenConns,
config.CgrConfig().StorDBMaxIdleConns,
config.CgrConfig().StorDBConnMaxLifetime,
@@ -122,36 +99,51 @@ func TestSessionCostITMySqlConnection(t *testing.T) {
if err != nil {
t.Error(err)
}
oldStorDB, err := ConfigureV1StorDB(sCostCfgIn.StorDBType,
sCostCfgIn.StorDBHost, sCostCfgIn.StorDBPort, sCostCfgIn.StorDBName,
sCostCfgIn.StorDBUser, sCostCfgIn.StorDBPass)
if err != nil {
log.Fatal(err)
}
sCostMigrator, err = NewMigrator(nil, nil, sCostCfgIn.DataDbType,
sCostCfgIn.DBDataEncoding, storDBIn, storDBOut, sCostCfgIn.StorDBType, nil,
sCostCfgIn.DataDbType, sCostCfgIn.DBDataEncoding, oldStorDB, sCostCfgIn.StorDBType,
false, false, false, false, false)
sCostMigrator, err = NewMigrator(nil, nil,
storDBIn, storDBOut,
false, false, false)
if err != nil {
t.Error(err)
}
}
func TestSessionCostITMySql(t *testing.T) {
for _, stest := range sTestssCostIT {
t.Run("TestSessionSCostITMigrateMySql", stest)
func testSessionCostITRename(t *testing.T) {
var err error
if err = sCostMigrator.storDBIn.createV1SMCosts(); err != nil {
t.Error(err)
}
currentVersion := engine.Versions{
utils.SessionSCosts: 1,
}
err = sCostMigrator.storDBOut.StorDB().SetVersions(currentVersion, false)
if err != nil {
t.Error("Error when setting version for SessionsCosts ", err.Error())
}
if vrs, err := sCostMigrator.storDBOut.StorDB().GetVersions(""); err != nil {
t.Error(err)
} else if vrs[utils.SessionSCosts] != 1 {
t.Errorf("Unexpected version returned: %d", vrs[utils.SessionSCosts])
}
err, _ = sCostMigrator.Migrate([]string{utils.MetaSessionsCosts})
if err != nil {
t.Error("Error when migrating SessionsCosts ", err.Error())
}
if vrs, err := sCostMigrator.storDBOut.StorDB().GetVersions(""); err != nil {
t.Error(err)
} else if vrs[utils.SessionSCosts] != 2 {
t.Errorf("Unexpected version returned: %d", vrs[utils.SessionSCosts])
}
}
func testSessionCostITFlush(t *testing.T) {
if err := sCostMigrator.storDBOut.Flush(
if err := sCostMigrator.storDBOut.StorDB().Flush(
path.Join(sCostCfgIn.DataFolderPath, "storage", sCostCfgIn.StorDBType)); err != nil {
t.Error(err)
}
}
func testSessionCostITMigrateAndMove(t *testing.T) {
func testSessionCostITMigrate(t *testing.T) {
cc := &engine.CallCost{
Direction: utils.OUT,
Cost: 1.23,
@@ -187,17 +179,17 @@ func testSessionCostITMigrateAndMove(t *testing.T) {
CostDetails: cc,
}
var err error
if err = sCostMigrator.oldStorDB.setSMCost(v2Cost); err != nil {
if err = sCostMigrator.storDBIn.setV2SMCost(v2Cost); err != nil {
t.Error(err)
}
currentVersion := engine.Versions{
utils.SessionSCosts: 2,
}
err = sCostMigrator.storDBOut.SetVersions(currentVersion, false)
err = sCostMigrator.storDBOut.StorDB().SetVersions(currentVersion, false)
if err != nil {
t.Error("Error when setting version for SessionsCosts ", err.Error())
}
if vrs, err := sCostMigrator.storDBOut.GetVersions(""); err != nil {
if vrs, err := sCostMigrator.storDBOut.StorDB().GetVersions(""); err != nil {
t.Error(err)
} else if vrs[utils.SessionSCosts] != 2 {
t.Errorf("Unexpected version returned: %d", vrs[utils.SessionSCosts])
@@ -206,15 +198,14 @@ func testSessionCostITMigrateAndMove(t *testing.T) {
if err != nil {
t.Error("Error when migrating SessionsCosts ", err.Error())
}
if rcvCosts, err := sCostMigrator.storDBOut.GetSMCosts("", utils.DEFAULT_RUNID, "", ""); err != nil {
if rcvCosts, err := sCostMigrator.storDBOut.StorDB().GetSMCosts("", utils.DEFAULT_RUNID, "", ""); err != nil {
t.Error(err)
} else if len(rcvCosts) != 1 {
t.Errorf("Unexpected number of SessionsCosts returned: %d", len(rcvCosts))
}
if vrs, err := sCostMigrator.storDBOut.GetVersions(""); err != nil {
if vrs, err := sCostMigrator.storDBOut.StorDB().GetVersions(""); err != nil {
t.Error(err)
} else if vrs[utils.SessionSCosts] != 3 {
t.Errorf("Unexpected version returned: %d", vrs[utils.SessionSCosts])
}
}
*/

View File

@@ -0,0 +1,226 @@
// +build integration
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package migrator
import (
"log"
"path"
"reflect"
"testing"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
var (
shrGrpPathIn string
shrGrpPathOut string
shrGrpCfgIn *config.CGRConfig
shrGrpCfgOut *config.CGRConfig
shrGrpMigrator *Migrator
shrSharedGroup string
)
var sTestsShrGrpIT = []func(t *testing.T){
testShrGrpITConnect,
testShrGrpITFlush,
testShrGrpITMigrateAndMove,
}
func TestSharedGroupITRedis(t *testing.T) {
var err error
shrGrpPathIn = path.Join(*dataDir, "conf", "samples", "tutmysql")
shrGrpCfgIn, err = config.NewCGRConfigFromFolder(shrGrpPathIn)
if err != nil {
t.Fatal(err)
}
shrGrpCfgOut, err = config.NewCGRConfigFromFolder(shrGrpPathIn)
if err != nil {
t.Fatal(err)
}
shrSharedGroup = utils.Migrate
for _, stest := range sTestsShrGrpIT {
t.Run("TestSharedGroupITMigrateRedis", stest)
}
}
func TestSharedGroupITMongo(t *testing.T) {
var err error
shrGrpPathIn = path.Join(*dataDir, "conf", "samples", "tutmongo")
shrGrpCfgIn, err = config.NewCGRConfigFromFolder(shrGrpPathIn)
if err != nil {
t.Fatal(err)
}
shrGrpCfgOut, err = config.NewCGRConfigFromFolder(shrGrpPathIn)
if err != nil {
t.Fatal(err)
}
shrSharedGroup = utils.Migrate
for _, stest := range sTestsShrGrpIT {
t.Run("TestSharedGroupITMigrateMongo", stest)
}
}
func TestSharedGroupITMove(t *testing.T) {
var err error
shrGrpPathIn = path.Join(*dataDir, "conf", "samples", "tutmongo")
shrGrpCfgIn, err = config.NewCGRConfigFromFolder(shrGrpPathIn)
if err != nil {
t.Fatal(err)
}
shrGrpPathOut = path.Join(*dataDir, "conf", "samples", "tutmysql")
shrGrpCfgOut, err = config.NewCGRConfigFromFolder(shrGrpPathOut)
if err != nil {
t.Fatal(err)
}
shrSharedGroup = utils.Move
for _, stest := range sTestsShrGrpIT {
t.Run("TestSharedGroupITMove", stest)
}
}
func TestSharedGroupITMoveEncoding(t *testing.T) {
var err error
shrGrpPathIn = path.Join(*dataDir, "conf", "samples", "tutmongojson")
shrGrpCfgIn, err = config.NewCGRConfigFromFolder(shrGrpPathIn)
if err != nil {
t.Fatal(err)
}
shrGrpPathOut = path.Join(*dataDir, "conf", "samples", "tutmongomsgpack")
shrGrpCfgOut, err = config.NewCGRConfigFromFolder(shrGrpPathOut)
if err != nil {
t.Fatal(err)
}
shrSharedGroup = utils.Move
for _, stest := range sTestsShrGrpIT {
t.Run("TestSharedGroupITMoveEncoding", stest)
}
}
func TestSharedGroupITMoveEncoding2(t *testing.T) {
var err error
shrGrpPathIn = path.Join(*dataDir, "conf", "samples", "tutmysqljson")
shrGrpCfgIn, err = config.NewCGRConfigFromFolder(shrGrpPathIn)
if err != nil {
t.Fatal(err)
}
shrGrpPathOut = path.Join(*dataDir, "conf", "samples", "tutmysqlmsgpack")
shrGrpCfgOut, err = config.NewCGRConfigFromFolder(shrGrpPathOut)
if err != nil {
t.Fatal(err)
}
shrSharedGroup = utils.Move
for _, stest := range sTestsShrGrpIT {
t.Run("TestSharedGroupITMoveEncoding2", stest)
}
}
func testShrGrpITConnect(t *testing.T) {
dataDBIn, err := NewMigratorDataDB(shrGrpCfgIn.DataDbType,
shrGrpCfgIn.DataDbHost, shrGrpCfgIn.DataDbPort, shrGrpCfgIn.DataDbName,
shrGrpCfgIn.DataDbUser, shrGrpCfgIn.DataDbPass, shrGrpCfgIn.DBDataEncoding,
config.CgrConfig().CacheCfg(), *loadHistorySize)
if err != nil {
log.Fatal(err)
}
dataDBOut, err := NewMigratorDataDB(shrGrpCfgOut.DataDbType,
shrGrpCfgOut.DataDbHost, shrGrpCfgOut.DataDbPort, shrGrpCfgOut.DataDbName,
shrGrpCfgOut.DataDbUser, shrGrpCfgOut.DataDbPass, shrGrpCfgOut.DBDataEncoding,
config.CgrConfig().CacheCfg(), *loadHistorySize)
if err != nil {
log.Fatal(err)
}
shrGrpMigrator, err = NewMigrator(dataDBIn, dataDBOut,
nil, nil,
false, false, false)
if err != nil {
log.Fatal(err)
}
}
func testShrGrpITFlush(t *testing.T) {
shrGrpMigrator.dmOut.DataManager().DataDB().Flush("")
if err := engine.SetDBVersions(shrGrpMigrator.dmOut.DataManager().DataDB()); err != nil {
t.Error("Error ", err.Error())
}
}
func testShrGrpITMigrateAndMove(t *testing.T) {
v1shrGrp := &v1SharedGroup{
Id: "Test",
AccountParameters: map[string]*engine.SharingParameters{
"test": &engine.SharingParameters{Strategy: "*highest"},
},
MemberIds: []string{"1", "2", "3"},
}
shrGrp := &engine.SharedGroup{
Id: "Test",
AccountParameters: map[string]*engine.SharingParameters{
"test": &engine.SharingParameters{Strategy: "*highest"},
},
MemberIds: utils.NewStringMap("1", "2", "3"),
}
switch shrSharedGroup {
case utils.Migrate:
err := shrGrpMigrator.dmIN.setV1SharedGroup(v1shrGrp)
if err != nil {
t.Error("Error when setting v1 SharedGroup ", err.Error())
}
currentVersion := engine.Versions{utils.StatS: 2, utils.Thresholds: 2, utils.Accounts: 2, utils.Actions: 2, utils.ActionTriggers: 2, utils.ActionPlans: 2, utils.SharedGroups: 1}
err = shrGrpMigrator.dmOut.DataManager().DataDB().SetVersions(currentVersion, false)
if err != nil {
t.Error("Error when setting version for SharedGroup ", err.Error())
}
err, _ = shrGrpMigrator.Migrate([]string{utils.MetaSharedGroups})
if err != nil {
t.Error("Error when migrating SharedGroup ", err.Error())
}
result, err := shrGrpMigrator.dmOut.DataManager().GetSharedGroup(v1shrGrp.Id, true, utils.NonTransactional)
if err != nil {
t.Error("Error when getting SharedGroup ", err.Error())
}
if !reflect.DeepEqual(shrGrp, result) {
t.Errorf("Expecting: %+v, received: %+v", shrGrp, result)
}
case utils.Move:
if err := shrGrpMigrator.dmIN.DataManager().SetSharedGroup(shrGrp, utils.NonTransactional); err != nil {
t.Error("Error when setting SharedGroup ", err.Error())
}
currentVersion := engine.CurrentDataDBVersions()
err := shrGrpMigrator.dmOut.DataManager().DataDB().SetVersions(currentVersion, false)
if err != nil {
t.Error("Error when setting version for SharedGroup ", err.Error())
}
err, _ = shrGrpMigrator.Migrate([]string{utils.MetaSharedGroups})
if err != nil {
t.Error("Error when migrating SharedGroup ", err.Error())
}
result, err := shrGrpMigrator.dmOut.DataManager().GetSharedGroup(v1shrGrp.Id, true, utils.NonTransactional)
if err != nil {
t.Error("Error when getting SharedGroup ", err.Error())
}
if !reflect.DeepEqual(shrGrp, result) {
t.Errorf("Expecting: %+v, received: %+v", shrGrp, result)
}
}
}

View File

@@ -20,7 +20,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package migrator
/*
import (
"log"
"path"
@@ -102,38 +101,31 @@ func TestStatsQueueITMove(t *testing.T) {
}
func testStsITConnect(t *testing.T) {
dataDBIn, err := engine.ConfigureDataStorage(stsCfgIn.DataDbType,
dataDBIn, err := NewMigratorDataDB(stsCfgIn.DataDbType,
stsCfgIn.DataDbHost, stsCfgIn.DataDbPort, stsCfgIn.DataDbName,
stsCfgIn.DataDbUser, stsCfgIn.DataDbPass, stsCfgIn.DBDataEncoding,
config.CgrConfig().CacheCfg(), *loadHistorySize)
if err != nil {
log.Fatal(err)
}
dataDBOut, err := engine.ConfigureDataStorage(stsCfgOut.DataDbType,
dataDBOut, err := NewMigratorDataDB(stsCfgOut.DataDbType,
stsCfgOut.DataDbHost, stsCfgOut.DataDbPort, stsCfgOut.DataDbName,
stsCfgOut.DataDbUser, stsCfgOut.DataDbPass, stsCfgOut.DBDataEncoding,
config.CgrConfig().CacheCfg(), *loadHistorySize)
if err != nil {
log.Fatal(err)
}
oldDataDB, err := ConfigureV1DataStorage(stsCfgIn.DataDbType,
stsCfgIn.DataDbHost, stsCfgIn.DataDbPort, stsCfgIn.DataDbName,
stsCfgIn.DataDbUser, stsCfgIn.DataDbPass, stsCfgIn.DBDataEncoding)
if err != nil {
log.Fatal(err)
}
stsMigrator, err = NewMigrator(dataDBIn, dataDBOut, stsCfgIn.DataDbType,
stsCfgIn.DBDataEncoding, nil, nil, stsCfgIn.StorDBType, oldDataDB,
stsCfgIn.DataDbType, stsCfgIn.DBDataEncoding, nil, stsCfgIn.StorDBType,
false, false, false, false, false)
stsMigrator, err = NewMigrator(dataDBIn, dataDBOut,
nil, nil,
false, false, false)
if err != nil {
log.Fatal(err)
}
}
func testStsITFlush(t *testing.T) {
stsMigrator.dmOut.DataDB().Flush("")
if err := engine.SetDBVersions(stsMigrator.dmOut.DataDB()); err != nil {
stsMigrator.dmOut.DataManager().DataDB().Flush("")
if err := engine.SetDBVersions(stsMigrator.dmOut.DataManager().DataDB()); err != nil {
t.Error("Error ", err.Error())
}
}
@@ -237,7 +229,7 @@ func testStsITMigrateAndMove(t *testing.T) {
}
switch stsAction {
case utils.Migrate:
err := stsMigrator.oldDataDB.setV1Stats(v1Sts)
err := stsMigrator.dmIN.setV1Stats(v1Sts)
if err != nil {
t.Error("Error when setting v1Stat ", err.Error())
}
@@ -249,7 +241,7 @@ func testStsITMigrateAndMove(t *testing.T) {
utils.ActionTriggers: 2,
utils.ActionPlans: 2,
utils.SharedGroups: 2}
err = stsMigrator.dmOut.DataDB().SetVersions(currentVersion, false)
err = stsMigrator.dmOut.DataManager().DataDB().SetVersions(currentVersion, false)
if err != nil {
t.Error("Error when setting version for stats ", err.Error())
}
@@ -258,8 +250,7 @@ func testStsITMigrateAndMove(t *testing.T) {
t.Error("Error when migrating Stats ", err.Error())
}
result, err := stsMigrator.dmOut.GetStatQueueProfile("cgrates.org",
v1Sts.Id, true, utils.NonTransactional)
result, err := stsMigrator.dmOut.DataManager().DataDB().GetStatQueueProfileDrv("cgrates.org", v1Sts.Id)
if err != nil {
t.Error("Error when getting Stats ", err.Error())
}
@@ -267,8 +258,7 @@ func testStsITMigrateAndMove(t *testing.T) {
t.Errorf("Expecting: %+v, received: %+v", sqp, result)
}
result1, err := stsMigrator.dmOut.GetFilter("cgrates.org",
v1Sts.Id, true, utils.NonTransactional)
result1, err := stsMigrator.dmOut.DataManager().DataDB().GetFilterDrv("cgrates.org", v1Sts.Id)
if err != nil {
t.Error("Error when getting Stats ", err.Error())
}
@@ -278,7 +268,7 @@ func testStsITMigrateAndMove(t *testing.T) {
t.Errorf("Expecting: %+v, received: %+v", len(filter.Rules), len(result1.Rules))
}
result2, err := stsMigrator.dmOut.GetStatQueue("cgrates.org", sq.ID, true, utils.NonTransactional)
result2, err := stsMigrator.dmOut.DataManager().GetStatQueue("cgrates.org", sq.ID, true, utils.NonTransactional)
if err != nil {
t.Error("Error when getting Stats ", err.Error())
}
@@ -287,14 +277,14 @@ func testStsITMigrateAndMove(t *testing.T) {
}
case utils.Move:
if err := stsMigrator.dmIN.SetStatQueueProfile(sqp, true); err != nil {
if err := stsMigrator.dmIN.DataManager().DataDB().SetStatQueueProfileDrv(sqp); err != nil {
t.Error("Error when setting Stats ", err.Error())
}
if err := stsMigrator.dmIN.SetStatQueue(sq); err != nil {
if err := stsMigrator.dmIN.DataManager().SetStatQueue(sq); err != nil {
t.Error("Error when setting Stats ", err.Error())
}
currentVersion := engine.CurrentDataDBVersions()
err := stsMigrator.dmOut.DataDB().SetVersions(currentVersion, false)
err := stsMigrator.dmOut.DataManager().DataDB().SetVersions(currentVersion, false)
if err != nil {
t.Error("Error when setting version for stats ", err.Error())
}
@@ -302,11 +292,11 @@ func testStsITMigrateAndMove(t *testing.T) {
if err != nil {
t.Error("Error when migrating Stats ", err.Error())
}
result, err := stsMigrator.dmOut.GetStatQueueProfile(sqp.Tenant, sqp.ID, true, utils.NonTransactional)
result, err := stsMigrator.dmOut.DataManager().DataDB().GetStatQueueProfileDrv(sqp.Tenant, sqp.ID)
if err != nil {
t.Error("Error when getting Stats ", err.Error())
}
result1, err := stsMigrator.dmOut.GetStatQueue(sq.Tenant, sq.ID, true, utils.NonTransactional)
result1, err := stsMigrator.dmOut.DataManager().GetStatQueue(sq.Tenant, sq.ID, true, utils.NonTransactional)
if err != nil {
t.Error("Error when getting Stats ", err.Error())
}
@@ -319,4 +309,3 @@ func testStsITMigrateAndMove(t *testing.T) {
}
}
*/

View File

@@ -22,6 +22,7 @@ import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
"github.com/cgrates/mgo"
"github.com/cgrates/mgo/bson"
)
func newMongoStorDBMigrator(stor engine.StorDB) (mgoMig *mongoStorDBMigrator) {
@@ -67,6 +68,23 @@ func (v1ms *mongoStorDBMigrator) setV1CDR(v1Cdr *v1Cdrs) (err error) {
}
//SMCost methods
//rename
func (v1ms *mongoStorDBMigrator) renameV1SMCosts() (err error) {
if err = v1ms.mgoDB.DB().C(utils.OldSMCosts).DropCollection(); err != nil {
return err
}
result := make(map[string]string)
return v1ms.mgoDB.DB().Run(bson.D{{"create", utils.SessionsCostsTBL}}, result)
}
func (v1ms *mongoStorDBMigrator) createV1SMCosts() (err error) {
err = v1ms.mgoDB.DB().C(utils.OldSMCosts).DropCollection()
err = v1ms.mgoDB.DB().C(utils.SessionsCostsTBL).DropCollection()
result := make(map[string]string)
return v1ms.mgoDB.DB().Run(bson.D{{"create", utils.OldSMCosts},
{"size", 1024}}, result)
}
//get
func (v1ms *mongoStorDBMigrator) getV2SMCost() (v2Cost *v2SessionsCost, err error) {
if v1ms.qryIter == nil {

View File

@@ -20,6 +20,7 @@ package migrator
import (
"database/sql"
"fmt"
"time"
"github.com/cgrates/cgrates/engine"
@@ -75,6 +76,48 @@ func (mgSQL *migratorSQL) setV1CDR(v1Cdr *v1Cdrs) (err error) {
return nil
}
func (mgSQL *migratorSQL) renameV1SMCosts() (err error) {
qry := "RENAME TABLE sm_costs TO sessions_costs;"
if mgSQL.StorDB().GetStorageType() == utils.POSTGRES {
qry = "ALTER TABLE sm_costs RENAME TO sessions_costs"
}
if _, err := mgSQL.sqlStorage.Db.Exec(qry); err != nil {
return err
}
return
}
func (mgSQL *migratorSQL) createV1SMCosts() (err error) {
qry := fmt.Sprint("CREATE TABLE sm_costs ( id int(11) NOT NULL AUTO_INCREMENT, cgrid varchar(40) NOT NULL, run_id varchar(64) NOT NULL, origin_host varchar(64) NOT NULL, origin_id varchar(128) NOT NULL, cost_source varchar(64) NOT NULL, `usage` BIGINT NOT NULL, cost_details MEDIUMTEXT, created_at TIMESTAMP NULL,deleted_at TIMESTAMP NULL, PRIMARY KEY (`id`),UNIQUE KEY costid (cgrid, run_id),KEY origin_idx (origin_host, origin_id),KEY run_origin_idx (run_id, origin_id),KEY deleted_at_idx (deleted_at));")
if mgSQL.StorDB().GetStorageType() == utils.POSTGRES {
qry = `
CREATE TABLE sm_costs (
id SERIAL PRIMARY KEY,
cgrid VARCHAR(40) NOT NULL,
run_id VARCHAR(64) NOT NULL,
origin_host VARCHAR(64) NOT NULL,
origin_id VARCHAR(128) NOT NULL,
cost_source VARCHAR(64) NOT NULL,
usage BIGINT NOT NULL,
cost_details jsonb,
created_at TIMESTAMP WITH TIME ZONE,
deleted_at TIMESTAMP WITH TIME ZONE NULL,
UNIQUE (cgrid, run_id)
);
`
}
if _, err := mgSQL.sqlStorage.Db.Exec("DROP TABLE IF EXISTS sessions_costs;"); err != nil {
return err
}
if _, err := mgSQL.sqlStorage.Db.Exec("DROP TABLE IF EXISTS sm_costs;"); err != nil {
return err
}
if _, err := mgSQL.sqlStorage.Db.Exec(qry); err != nil {
return err
}
return
}
func (mgSQL *migratorSQL) getV2SMCost() (v2Cost *v2SessionsCost, err error) {
if mgSQL.rowIter == nil {
mgSQL.rowIter, err = mgSQL.sqlStorage.Db.Query("SELECT * FROM sessions_costs")

View File

@@ -0,0 +1,265 @@
// +build integration
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package migrator
import (
"log"
"path"
"reflect"
"testing"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
var (
trsPathIn string
trsPathOut string
trsCfgIn *config.CGRConfig
trsCfgOut *config.CGRConfig
trsMigrator *Migrator
trsThresholds string
)
var sTestsTrsIT = []func(t *testing.T){
testTrsITConnect,
testTrsITFlush,
testTrsITMigrateAndMove,
}
func TestThresholdsITRedis(t *testing.T) {
var err error
trsPathIn = path.Join(*dataDir, "conf", "samples", "tutmysql")
trsCfgIn, err = config.NewCGRConfigFromFolder(trsPathIn)
if err != nil {
t.Fatal(err)
}
trsCfgOut, err = config.NewCGRConfigFromFolder(trsPathIn)
if err != nil {
t.Fatal(err)
}
trsThresholds = utils.Migrate
for _, stest := range sTestsTrsIT {
t.Run("TestThresholdsITMigrateRedis", stest)
}
}
func TestThresholdsITMongo(t *testing.T) {
var err error
trsPathIn = path.Join(*dataDir, "conf", "samples", "tutmongo")
trsCfgIn, err = config.NewCGRConfigFromFolder(trsPathIn)
if err != nil {
t.Fatal(err)
}
trsCfgOut, err = config.NewCGRConfigFromFolder(trsPathIn)
if err != nil {
t.Fatal(err)
}
trsThresholds = utils.Migrate
for _, stest := range sTestsTrsIT {
t.Run("TestThresholdsITMigrateMongo", stest)
}
}
func TestThresholdsITMove(t *testing.T) {
var err error
trsPathIn = path.Join(*dataDir, "conf", "samples", "tutmongo")
trsCfgIn, err = config.NewCGRConfigFromFolder(trsPathIn)
if err != nil {
t.Fatal(err)
}
trsPathOut = path.Join(*dataDir, "conf", "samples", "tutmysql")
trsCfgOut, err = config.NewCGRConfigFromFolder(trsPathOut)
if err != nil {
t.Fatal(err)
}
trsThresholds = utils.Move
for _, stest := range sTestsTrsIT {
t.Run("TestThresholdsITMove", stest)
}
}
func TestThresholdsITMoveEncoding(t *testing.T) {
var err error
trsPathIn = path.Join(*dataDir, "conf", "samples", "tutmongojson")
trsCfgIn, err = config.NewCGRConfigFromFolder(trsPathIn)
if err != nil {
t.Fatal(err)
}
trsPathOut = path.Join(*dataDir, "conf", "samples", "tutmongomsgpack")
trsCfgOut, err = config.NewCGRConfigFromFolder(trsPathOut)
if err != nil {
t.Fatal(err)
}
trsThresholds = utils.Move
for _, stest := range sTestsTrsIT {
t.Run("TestThresholdsITMoveEncoding", stest)
}
}
func TestThresholdsITMoveEncoding2(t *testing.T) {
var err error
trsPathIn = path.Join(*dataDir, "conf", "samples", "tutmysqljson")
trsCfgIn, err = config.NewCGRConfigFromFolder(trsPathIn)
if err != nil {
t.Fatal(err)
}
trsPathOut = path.Join(*dataDir, "conf", "samples", "tutmysqlmsgpack")
trsCfgOut, err = config.NewCGRConfigFromFolder(trsPathOut)
if err != nil {
t.Fatal(err)
}
trsThresholds = utils.Move
for _, stest := range sTestsTrsIT {
t.Run("TestThresholdsITMoveEncoding2", stest)
}
}
func testTrsITConnect(t *testing.T) {
dataDBIn, err := NewMigratorDataDB(trsCfgIn.DataDbType,
trsCfgIn.DataDbHost, trsCfgIn.DataDbPort, trsCfgIn.DataDbName,
trsCfgIn.DataDbUser, trsCfgIn.DataDbPass, trsCfgIn.DBDataEncoding,
config.CgrConfig().CacheCfg(), *loadHistorySize)
if err != nil {
log.Fatal(err)
}
dataDBOut, err := NewMigratorDataDB(trsCfgOut.DataDbType,
trsCfgOut.DataDbHost, trsCfgOut.DataDbPort, trsCfgOut.DataDbName,
trsCfgOut.DataDbUser, trsCfgOut.DataDbPass, trsCfgOut.DBDataEncoding,
config.CgrConfig().CacheCfg(), *loadHistorySize)
if err != nil {
log.Fatal(err)
}
trsMigrator, err = NewMigrator(dataDBIn, dataDBOut,
nil, nil,
false, false, false)
if err != nil {
log.Fatal(err)
}
}
func testTrsITFlush(t *testing.T) {
trsMigrator.dmOut.DataManager().DataDB().Flush("")
if err := engine.SetDBVersions(trsMigrator.dmOut.DataManager().DataDB()); err != nil {
t.Error("Error ", err.Error())
}
}
func testTrsITMigrateAndMove(t *testing.T) {
tim := time.Date(2012, time.February, 27, 23, 59, 59, 0, time.UTC)
var filters []*engine.FilterRule
v1trs := &v2ActionTrigger{
ID: "test2", // original csv tag
UniqueID: "testUUID", // individual id
ThresholdType: "*min_event_counter", //*min_event_counter, *max_event_counter, *min_balance_counter, *max_balance_counter, *min_balance, *max_balance, *balance_expired
ThresholdValue: 5.32,
Recurrent: false, // reset excuted flag each run
MinSleep: time.Duration(5) * time.Second, // Minimum duration between two executions in case of recurrent triggers
ExpirationDate: tim,
ActivationDate: tim,
Balance: &engine.BalanceFilter{
ID: utils.StringPointer("TESTZ"),
Timings: []*engine.RITiming{},
ExpirationDate: utils.TimePointer(tim),
Type: utils.StringPointer(utils.MONETARY),
Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)),
},
Weight: 0,
ActionsID: "Action1",
MinQueuedItems: 10, // Trigger actions only if this number is hit (stats only)
Executed: false,
LastExecutionTime: time.Now(),
}
x, err := engine.NewFilterRule(engine.MetaRSR, "Directions", v1trs.Balance.Directions.Slice())
if err != nil {
t.Error("Error when creating new NewFilterRule", err.Error())
}
filters = append(filters, x)
tresProf := &engine.ThresholdProfile{
ID: v1trs.ID,
Tenant: config.CgrConfig().DefaultTenant,
Weight: v1trs.Weight,
ActivationInterval: &utils.ActivationInterval{v1trs.ExpirationDate, v1trs.ActivationDate},
MinSleep: v1trs.MinSleep,
}
switch trsThresholds {
case utils.Migrate:
err := trsMigrator.dmIN.setV2ActionTrigger(v1trs)
if err != nil {
t.Error("Error when setting v1 Thresholds ", err.Error())
}
currentVersion := engine.Versions{utils.StatS: 2, utils.Thresholds: 1, utils.Accounts: 2, utils.Actions: 2, utils.ActionTriggers: 2, utils.ActionPlans: 2, utils.SharedGroups: 2}
err = trsMigrator.dmOut.DataManager().DataDB().SetVersions(currentVersion, false)
if err != nil {
t.Error("Error when setting version for Thresholds ", err.Error())
}
err, _ = trsMigrator.Migrate([]string{utils.MetaThresholds})
if err != nil {
t.Error("Error when migrating Thresholds ", err.Error())
}
result, err := trsMigrator.dmOut.DataManager().GetThresholdProfile(tresProf.Tenant, tresProf.ID, false, utils.NonTransactional)
if err != nil {
t.Error("Error when getting Thresholds ", err.Error())
}
if !reflect.DeepEqual(tresProf.ID, result.ID) {
t.Errorf("Expecting: %+v, received: %+v", tresProf.ID, result.ID)
} else if !reflect.DeepEqual(tresProf.Tenant, result.Tenant) {
t.Errorf("Expecting: %+v, received: %+v", tresProf.Tenant, result.Tenant)
} else if !reflect.DeepEqual(tresProf.Weight, result.Weight) {
t.Errorf("Expecting: %+v, received: %+v", tresProf.Weight, result.Weight)
} else if !reflect.DeepEqual(tresProf.ActivationInterval, result.ActivationInterval) {
t.Errorf("Expecting: %+v, received: %+v", tresProf.ActivationInterval, result.ActivationInterval)
} else if !reflect.DeepEqual(tresProf.MinSleep, result.MinSleep) {
t.Errorf("Expecting: %+v, received: %+v", tresProf.MinSleep, result.MinSleep)
}
case utils.Move:
if err := trsMigrator.dmIN.DataManager().SetThresholdProfile(tresProf, false); err != nil {
t.Error("Error when setting Thresholds ", err.Error())
}
currentVersion := engine.CurrentDataDBVersions()
err := trsMigrator.dmOut.DataManager().DataDB().SetVersions(currentVersion, false)
if err != nil {
t.Error("Error when setting version for Thresholds ", err.Error())
}
err, _ = trsMigrator.Migrate([]string{utils.MetaSharedGroups})
if err != nil {
t.Error("Error when migrating Thresholds ", err.Error())
}
result, err := trsMigrator.dmOut.DataManager().GetThresholdProfile(tresProf.Tenant, tresProf.ID, false, utils.NonTransactional)
if err != nil {
t.Error("Error when getting Thresholds ", err.Error())
}
if !reflect.DeepEqual(tresProf.ID, result.ID) {
t.Errorf("Expecting: %+v, received: %+v", tresProf.ID, result.ID)
} else if !reflect.DeepEqual(tresProf.Tenant, result.Tenant) {
t.Errorf("Expecting: %+v, received: %+v", tresProf.Tenant, result.Tenant)
} else if !reflect.DeepEqual(tresProf.Weight, result.Weight) {
t.Errorf("Expecting: %+v, received: %+v", tresProf.Weight, result.Weight)
} else if !reflect.DeepEqual(tresProf.ActivationInterval, result.ActivationInterval) {
t.Errorf("Expecting: %+v, received: %+v", tresProf.ActivationInterval, result.ActivationInterval)
} else if !reflect.DeepEqual(tresProf.MinSleep, result.MinSleep) {
t.Errorf("Expecting: %+v, received: %+v", tresProf.MinSleep, result.MinSleep)
}
}
}

View File

@@ -535,6 +535,7 @@ const (
Freeswitch = "freeswitch"
Kamailio = "kamailio"
Opensips = "opensips"
Asterisk = "asterisk"
)
// Migrator Action
@@ -778,6 +779,7 @@ const (
TBLTPSuppliers = "tp_suppliers"
TBLTPAttributes = "tp_attributes"
TBLVersions = "versions"
OldSMCosts = "sm_costs"
)
// Cache Name
@@ -841,6 +843,7 @@ const (
RadiusAgent = "RadiusAgent"
DiameterAgent = "DiameterAgent"
FreeSWITCHAgent = "FreeSWITCHAgent"
AsteriskAgent = "AsteriskAgent"
)
func buildCacheInstRevPrefixes() {