Merge pull request #1 from cgrates/master

test
This commit is contained in:
rbarrabe
2016-05-03 15:12:16 +02:00
212 changed files with 8961 additions and 3791 deletions

View File

@@ -7,7 +7,7 @@ install:
- go get github.com/Masterminds/glide
- glide install
script: $TRAVIS_BUILD_DIR/test.sh
script: go test -v $(glide novendor)
branches:
only: master

View File

@@ -42,6 +42,7 @@ information, please see the [`CONTRIBUTING.md`](CONTRIBUTING.md) file.
| @bhepp | Brice Heppner |
| @noahmehl | Noah Mehl |
| @elfranne | Tom Braarup Cuykens |
| @rbarrabe | Régis Barrabé |
<!-- to sign, include a single line above this comment containing the following text:
| @username | First Last |
-->

View File

@@ -32,7 +32,7 @@ import (
"github.com/fiorix/go-diameter/diam/sm"
)
func NewDiameterAgent(cgrCfg *config.CGRConfig, smg *rpcclient.RpcClient, pubsubs *rpcclient.RpcClient) (*DiameterAgent, error) {
func NewDiameterAgent(cgrCfg *config.CGRConfig, smg rpcclient.RpcClientConnection, pubsubs rpcclient.RpcClientConnection) (*DiameterAgent, error) {
da := &DiameterAgent{cgrCfg: cgrCfg, smg: smg, pubsubs: pubsubs}
dictsDir := cgrCfg.DiameterAgentCfg().DictionariesDir
if len(dictsDir) != 0 {
@@ -45,8 +45,8 @@ func NewDiameterAgent(cgrCfg *config.CGRConfig, smg *rpcclient.RpcClient, pubsub
type DiameterAgent struct {
cgrCfg *config.CGRConfig
smg *rpcclient.RpcClient // Connection towards CGR-SMG component
pubsubs *rpcclient.RpcClient // Connection towards CGR-PubSub component
smg rpcclient.RpcClientConnection // Connection towards CGR-SMG component
pubsubs rpcclient.RpcClientConnection // Connection towards CGR-PubSub component
}
// Creates the message handlers
@@ -69,7 +69,7 @@ func (self *DiameterAgent) handlers() diam.Handler {
return dSM
}
func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestProcessor, cca *CCA) (bool, error) {
func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestProcessor, processorVars map[string]string, cca *CCA) (bool, error) {
passesAllFilters := true
for _, fldFilter := range reqProcessor.RequestFilter {
if passes, _ := passesFieldFilter(ccr.diamMessage, fldFilter, nil); !passes {
@@ -122,7 +122,6 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro
}
}
var maxUsage float64
processorVars := make(map[string]string)
processorVars[CGRResultCode] = strconv.Itoa(diam.Success)
processorVars[CGRError] = ""
if reqProcessor.DryRun { // DryRun does not send over network
@@ -166,6 +165,15 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro
processorVars[CGRResultCode] = strconv.Itoa(DiameterRatingFailed)
}
}
if maxUsage < 0 {
maxUsage = 0
}
if prevMaxUsageStr, hasKey := processorVars[CGRMaxUsage]; hasKey {
prevMaxUsage, _ := strconv.ParseFloat(prevMaxUsageStr, 64)
if prevMaxUsage < maxUsage {
maxUsage = prevMaxUsage
}
}
processorVars[CGRMaxUsage] = strconv.FormatFloat(maxUsage, 'f', -1, 64)
}
if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, processorVars[CGRResultCode],
@@ -190,10 +198,14 @@ func (self *DiameterAgent) handleCCR(c diam.Conn, m *diam.Message) {
return
}
cca := NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm)
var processed bool
var processed, lclProcessed bool
processorVars := make(map[string]string) // Shared between processors
for _, reqProcessor := range self.cgrCfg.DiameterAgentCfg().RequestProcessors {
processed, err = self.processCCR(ccr, reqProcessor, cca)
if err != nil || (processed && !reqProcessor.ContinueOnSuccess) {
lclProcessed, err = self.processCCR(ccr, reqProcessor, processorVars, cca)
if lclProcessed { // Process local so we don't overwrite globally
processed = lclProcessed
}
if err != nil || (lclProcessed && !reqProcessor.ContinueOnSuccess) {
break
}
}

View File

@@ -208,7 +208,7 @@ func TestDmtAgentTPFromFolder(t *testing.T) {
if err := apierRpc.Call("ApierV2.LoadTariffPlanFromFolder", attrs, &loadInst); err != nil {
t.Error(err)
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups
time.Sleep(time.Duration(1000) * time.Millisecond) // Give time for scheduler to execute topups
}
func TestConnectDiameterClient(t *testing.T) {
@@ -242,7 +242,7 @@ func TestDmtAgentSendCCRInit(t *testing.T) {
if err := dmtClient.SendMessage(m); err != nil {
t.Error(err)
}
time.Sleep(time.Duration(100) * time.Millisecond)
time.Sleep(time.Duration(*waitRater) * time.Millisecond)
msg := dmtClient.ReceivedMessage()
if avps, err := msg.FindAVPsWithPath([]interface{}{"Granted-Service-Unit", "CC-Time"}, dict.UndefinedVendorID); err != nil {
t.Error(err)
@@ -286,7 +286,7 @@ func TestDmtAgentSendCCRUpdate(t *testing.T) {
if err := dmtClient.SendMessage(m); err != nil {
t.Error(err)
}
time.Sleep(time.Duration(100) * time.Millisecond)
time.Sleep(time.Duration(*waitRater) * time.Millisecond)
msg := dmtClient.ReceivedMessage()
if avps, err := msg.FindAVPsWithPath([]interface{}{"Granted-Service-Unit", "CC-Time"}, dict.UndefinedVendorID); err != nil {
t.Error(err)
@@ -325,7 +325,7 @@ func TestDmtAgentSendCCRUpdate2(t *testing.T) {
if err := dmtClient.SendMessage(m); err != nil {
t.Error(err)
}
time.Sleep(time.Duration(100) * time.Millisecond)
time.Sleep(time.Duration(*waitRater) * time.Millisecond)
msg := dmtClient.ReceivedMessage()
if avps, err := msg.FindAVPsWithPath([]interface{}{"Granted-Service-Unit", "CC-Time"}, dict.UndefinedVendorID); err != nil {
t.Error(err)
@@ -363,7 +363,7 @@ func TestDmtAgentSendCCRTerminate(t *testing.T) {
if err := dmtClient.SendMessage(m); err != nil {
t.Error(err)
}
time.Sleep(time.Duration(150) * time.Millisecond)
time.Sleep(time.Duration(*waitRater) * time.Millisecond)
msg := dmtClient.ReceivedMessage()
if msg == nil {
t.Fatal("No answer to CCR terminate received")
@@ -626,7 +626,7 @@ func TestDmtAgentSendCCRSimpaEvent(t *testing.T) {
}),
diam.NewAVP(29000, avp.Mbit, 2011, &diam.GroupedAVP{ // MC-Information
AVP: []*diam.AVP{
diam.NewAVP(29001, avp.Mbit, 2011, datatype.OctetString("0x38924012914528")), // HighLayerCharacteristics
diam.NewAVP(20938, avp.Mbit, 2011, datatype.OctetString("0x38924012914528")), // HighLayerCharacteristics
diam.NewAVP(29002, avp.Mbit, 2011, datatype.UTF8String("12928471313847173")), // MC-Service-Id
diam.NewAVP(29003, avp.Mbit, 2011, datatype.UTF8String("SPV123456012123")), // TransparentData
diam.NewAVP(1201, avp.Mbit, 10415, &diam.GroupedAVP{ // MC-Information
@@ -641,7 +641,7 @@ func TestDmtAgentSendCCRSimpaEvent(t *testing.T) {
if err := dmtClient.SendMessage(ccr); err != nil {
t.Error(err)
}
time.Sleep(time.Duration(100) * time.Millisecond)
time.Sleep(time.Duration(*waitRater) * time.Millisecond)
msg := dmtClient.ReceivedMessage() // Discard the received message so we can test next one
if msg == nil {
t.Fatal("No message returned")
@@ -675,6 +675,261 @@ func TestDmtAgentCdrs(t *testing.T) {
}
}
func TestDmtAgentSendDataGrpInit(t *testing.T) {
if !*testIntegration {
return
}
ccr := diam.NewRequest(diam.CreditControl, 4, nil)
ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("testdatagrp"))
ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA"))
ccr.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org"))
ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4))
ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("gprs@huawei.com"))
ccr.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(1))
ccr.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(1))
ccr.NewAVP(avp.EventTimestamp, avp.Mbit, 0, datatype.Time(time.Date(2016, 1, 5, 11, 30, 10, 0, time.UTC)))
ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{
AVP: []*diam.AVP{
diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(0)),
diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("1001")), // Subscription-Id-Data
}})
ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{
AVP: []*diam.AVP{
diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(1)),
diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("104502200011")), // Subscription-Id-Data
}})
ccr.NewAVP(avp.ServiceIdentifier, avp.Mbit, 0, datatype.Unsigned32(0))
ccr.NewAVP(avp.RequestedAction, avp.Mbit, 0, datatype.Enumerated(0))
ccr.NewAVP(avp.RequestedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{
AVP: []*diam.AVP{
diam.NewAVP(avp.CCTime, avp.Mbit, 0, datatype.Unsigned32(1))}})
ccr.NewAVP(873, avp.Mbit, 10415, &diam.GroupedAVP{ //
AVP: []*diam.AVP{
diam.NewAVP(20300, avp.Mbit, 2011, &diam.GroupedAVP{ // IN-Information
AVP: []*diam.AVP{
diam.NewAVP(20302, avp.Mbit, 2011, datatype.UTF8String("22509")), // Calling-Vlr-Number
diam.NewAVP(20385, avp.Mbit, 2011, datatype.UTF8String("4002")), // Called-Party-NP
},
}),
diam.NewAVP(2000, avp.Mbit, 10415, &diam.GroupedAVP{ // SMS-Information
AVP: []*diam.AVP{
diam.NewAVP(886, avp.Mbit, 10415, &diam.GroupedAVP{ // Originator-Address
AVP: []*diam.AVP{
diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type
diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("1003")), // Address-Data
}}),
diam.NewAVP(1201, avp.Mbit, 10415, &diam.GroupedAVP{ // Recipient-Address
AVP: []*diam.AVP{
diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type
diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("1002")), // Address-Data
}}),
},
}),
}})
if err := dmtClient.SendMessage(ccr); err != nil {
t.Error(err)
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond)
msg := dmtClient.ReceivedMessage()
if msg == nil {
t.Fatal("No message returned")
}
if avps, err := msg.FindAVPsWithPath([]interface{}{"Result-Code"}, dict.UndefinedVendorID); err != nil {
t.Error(err)
} else if len(avps) == 0 {
t.Error("Result-Code not found")
} else if resCode := avpValAsString(avps[0]); resCode != "2001" {
t.Errorf("Expecting 2001, received: %s", resCode)
}
}
func TestDmtAgentSendDataGrpUpdate(t *testing.T) {
if !*testIntegration {
return
}
ccr := diam.NewRequest(diam.CreditControl, 4, nil)
ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("testdatagrp"))
ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA"))
ccr.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org"))
ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4))
ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("gprs@huawei.com"))
ccr.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(2))
ccr.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(1))
ccr.NewAVP(avp.EventTimestamp, avp.Mbit, 0, datatype.Time(time.Date(2016, 1, 5, 11, 30, 10, 0, time.UTC)))
ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{
AVP: []*diam.AVP{
diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(0)),
diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("1001")), // Subscription-Id-Data
}})
ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{
AVP: []*diam.AVP{
diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(1)),
diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("104502200011")), // Subscription-Id-Data
}})
ccr.NewAVP(avp.ServiceIdentifier, avp.Mbit, 0, datatype.Unsigned32(0))
ccr.NewAVP(avp.RequestedAction, avp.Mbit, 0, datatype.Enumerated(0))
ccr.NewAVP(avp.RequestedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{
AVP: []*diam.AVP{
diam.NewAVP(avp.CCTime, avp.Mbit, 0, datatype.Unsigned32(1))}})
ccr.NewAVP(873, avp.Mbit, 10415, &diam.GroupedAVP{ //
AVP: []*diam.AVP{
diam.NewAVP(20300, avp.Mbit, 2011, &diam.GroupedAVP{ // IN-Information
AVP: []*diam.AVP{
diam.NewAVP(20302, avp.Mbit, 2011, datatype.UTF8String("22509")), // Calling-Vlr-Number
diam.NewAVP(20385, avp.Mbit, 2011, datatype.UTF8String("4002")), // Called-Party-NP
},
}),
diam.NewAVP(2000, avp.Mbit, 10415, &diam.GroupedAVP{ // SMS-Information
AVP: []*diam.AVP{
diam.NewAVP(886, avp.Mbit, 10415, &diam.GroupedAVP{ // Originator-Address
AVP: []*diam.AVP{
diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type
diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("1003")), // Address-Data
}}),
diam.NewAVP(1201, avp.Mbit, 10415, &diam.GroupedAVP{ // Recipient-Address
AVP: []*diam.AVP{
diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type
diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("1002")), // Address-Data
}}),
},
}),
}})
ccr.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
AVP: []*diam.AVP{
diam.NewAVP(446, avp.Mbit, 0, &diam.GroupedAVP{ // Used-Service-Unit
AVP: []*diam.AVP{
diam.NewAVP(452, avp.Mbit, 0, datatype.Enumerated(0)), // Tariff-Change-Usage
diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(20)), // CC-Time
diam.NewAVP(412, avp.Mbit, 0, datatype.Unsigned64(1000)), // CC-Input-Octets
diam.NewAVP(414, avp.Mbit, 0, datatype.Unsigned64(24)), // CC-Output-Octets
},
}),
diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(1)), // Data session for group 1
},
})
ccr.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
AVP: []*diam.AVP{
diam.NewAVP(446, avp.Mbit, 0, &diam.GroupedAVP{ // Used-Service-Unit
AVP: []*diam.AVP{
diam.NewAVP(452, avp.Mbit, 0, datatype.Enumerated(0)), // Tariff-Change-Usage
diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(20)), // CC-Time
diam.NewAVP(412, avp.Mbit, 0, datatype.Unsigned64(1024)), // CC-Input-Octets
diam.NewAVP(414, avp.Mbit, 0, datatype.Unsigned64(512)), // CC-Output-Octets
},
}),
diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(2)), // Data session for group 2
},
})
if err := dmtClient.SendMessage(ccr); err != nil {
t.Error(err)
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond)
msg := dmtClient.ReceivedMessage()
if msg == nil {
t.Fatal("No message returned")
}
if avps, err := msg.FindAVPsWithPath([]interface{}{"Result-Code"}, dict.UndefinedVendorID); err != nil {
t.Error(err)
} else if len(avps) == 0 {
t.Error("Result-Code not found")
} else if resCode := avpValAsString(avps[0]); resCode != "2001" {
t.Errorf("Expecting 2001, received: %s", resCode)
}
}
func TestDmtAgentSendDataGrpTerminate(t *testing.T) {
if !*testIntegration {
return
}
ccr := diam.NewRequest(diam.CreditControl, 4, nil)
ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("testdatagrp"))
ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA"))
ccr.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org"))
ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4))
ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("gprs@huawei.com"))
ccr.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(3))
ccr.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(1))
ccr.NewAVP(avp.EventTimestamp, avp.Mbit, 0, datatype.Time(time.Date(2016, 1, 5, 11, 30, 10, 0, time.UTC)))
ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{
AVP: []*diam.AVP{
diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(0)),
diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("1001")), // Subscription-Id-Data
}})
ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{
AVP: []*diam.AVP{
diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(1)),
diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("104502200011")), // Subscription-Id-Data
}})
ccr.NewAVP(avp.ServiceIdentifier, avp.Mbit, 0, datatype.Unsigned32(0))
ccr.NewAVP(avp.RequestedAction, avp.Mbit, 0, datatype.Enumerated(0))
ccr.NewAVP(avp.RequestedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{
AVP: []*diam.AVP{
diam.NewAVP(avp.CCTime, avp.Mbit, 0, datatype.Unsigned32(1))}})
ccr.NewAVP(873, avp.Mbit, 10415, &diam.GroupedAVP{ //
AVP: []*diam.AVP{
diam.NewAVP(20300, avp.Mbit, 2011, &diam.GroupedAVP{ // IN-Information
AVP: []*diam.AVP{
diam.NewAVP(20302, avp.Mbit, 2011, datatype.UTF8String("22509")), // Calling-Vlr-Number
diam.NewAVP(20385, avp.Mbit, 2011, datatype.UTF8String("4002")), // Called-Party-NP
},
}),
diam.NewAVP(2000, avp.Mbit, 10415, &diam.GroupedAVP{ // SMS-Information
AVP: []*diam.AVP{
diam.NewAVP(886, avp.Mbit, 10415, &diam.GroupedAVP{ // Originator-Address
AVP: []*diam.AVP{
diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type
diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("1003")), // Address-Data
}}),
diam.NewAVP(1201, avp.Mbit, 10415, &diam.GroupedAVP{ // Recipient-Address
AVP: []*diam.AVP{
diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type
diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("1002")), // Address-Data
}}),
},
}),
}})
ccr.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
AVP: []*diam.AVP{
diam.NewAVP(446, avp.Mbit, 0, &diam.GroupedAVP{ // Used-Service-Unit
AVP: []*diam.AVP{
diam.NewAVP(452, avp.Mbit, 0, datatype.Enumerated(0)), // Tariff-Change-Usage
diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(20)), // CC-Time
diam.NewAVP(412, avp.Mbit, 0, datatype.Unsigned64(512)), // CC-Input-Octets
diam.NewAVP(414, avp.Mbit, 0, datatype.Unsigned64(0)), // CC-Output-Octets
},
}),
},
})
if err := dmtClient.SendMessage(ccr); err != nil {
t.Error(err)
}
time.Sleep(time.Duration(3000) * time.Millisecond)
msg := dmtClient.ReceivedMessage()
if msg == nil {
t.Fatal("No message returned")
}
if avps, err := msg.FindAVPsWithPath([]interface{}{"Result-Code"}, dict.UndefinedVendorID); err != nil {
t.Error(err)
} else if len(avps) == 0 {
t.Error("Result-Code not found")
} else if resCode := avpValAsString(avps[0]); resCode != "2001" {
t.Errorf("Expecting 2001, received: %s", resCode)
}
}
func TestDmtAgentSendDataGrpCDRs(t *testing.T) {
if !*testIntegration {
return
}
var cdrs []*engine.ExternalCDR
req := utils.RPCCDRsFilter{CGRIDs: []string{utils.Sha1("testdatagrp")}}
if err := apierRpc.Call("ApierV2.GetCdrs", req, &cdrs); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(cdrs) != 3 {
t.Error("Unexpected number of CDRs returned: ", len(cdrs))
}
}
func TestDmtAgentDryRun1(t *testing.T) {
if !*testIntegration {
return

124
agents/hapool_it_test.go Normal file
View File

@@ -0,0 +1,124 @@
/*
Real-time Charging System for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can Storagetribute 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 WITH*out 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 agents
/*
import (
"os/exec"
"path"
"testing"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
)
var cgrRater1Cmd, cgrSmg1Cmd *exec.Cmd
func TestHaPoolInitCfg(t *testing.T) {
if !*testIntegration {
return
}
daCfgPath = path.Join(*dataDir, "conf", "samples", "hapool", "cgrrater1")
// Init config first
var err error
daCfg, err = config.NewCGRConfigFromFolder(daCfgPath)
if err != nil {
t.Error(err)
}
daCfg.DataFolderPath = *dataDir // Share DataFolderPath through config towards StoreDb for Flush()
config.SetCgrConfig(daCfg)
}
// Remove data in both rating and accounting db
func TestHaPoolResetDataDb(t *testing.T) {
TestDmtAgentResetDataDb(t)
}
// Wipe out the cdr database
func TestHaPoolResetStorDb(t *testing.T) {
TestDmtAgentResetStorDb(t)
}
// Start CGR Engine
func TestHaPoolStartEngine(t *testing.T) {
if !*testIntegration {
return
}
engine.KillEngine(*waitRater) // just to make sure
var err error
cgrRater1 := path.Join(*dataDir, "conf", "samples", "hapool", "cgrrater1")
if cgrRater1Cmd, err = engine.StartEngine(cgrRater1, *waitRater); err != nil {
t.Fatal("cgrRater1: ", err)
}
cgrRater2 := path.Join(*dataDir, "conf", "samples", "hapool", "cgrrater2")
if _, err = engine.StartEngine(cgrRater2, *waitRater); err != nil {
t.Fatal("cgrRater2: ", err)
}
cgrSmg1 := path.Join(*dataDir, "conf", "samples", "hapool", "cgrsmg1")
if cgrSmg1Cmd, err = engine.StartEngine(cgrSmg1, *waitRater); err != nil {
t.Fatal("cgrSmg1: ", err)
}
cgrSmg2 := path.Join(*dataDir, "conf", "samples", "hapool", "cgrsmg2")
if _, err = engine.StartEngine(cgrSmg2, *waitRater); err != nil {
t.Fatal("cgrSmg2: ", err)
}
cgrDa := path.Join(*dataDir, "conf", "samples", "hapool", "dagent")
if _, err = engine.StartEngine(cgrDa, *waitRater); err != nil {
t.Fatal("cgrDa: ", err)
}
}
// Connect rpc client to rater
func TestHaPoolApierRpcConn(t *testing.T) {
TestDmtAgentApierRpcConn(t)
}
// Load the tariff plan, creating accounts and their balances
func TestHaPoolTPFromFolder(t *testing.T) {
TestDmtAgentTPFromFolder(t)
}
// cgr-console 'cost Category="call" Tenant="cgrates.org" Subject="1001" Destination="1004" TimeStart="2015-11-07T08:42:26Z" TimeEnd="2015-11-07T08:47:26Z"'
func TestHaPoolSendCCRInit(t *testing.T) {
TestDmtAgentSendCCRInit(t)
}
// cgr-console 'cost Category="call" Tenant="cgrates.org" Subject="1001" Destination="1004" TimeStart="2015-11-07T08:42:26Z" TimeEnd="2015-11-07T08:52:26Z"'
func TestHaPoolSendCCRUpdate(t *testing.T) {
TestDmtAgentSendCCRUpdate(t)
}
// cgr-console 'cost Category="call" Tenant="cgrates.org" Subject="1001" Destination="1004" TimeStart="2015-11-07T08:42:26Z" TimeEnd="2015-11-07T08:57:26Z"'
func TestHaPoolSendCCRUpdate2(t *testing.T) {
TestDmtAgentSendCCRUpdate2(t)
}
func TestHaPoolSendCCRTerminate(t *testing.T) {
TestDmtAgentSendCCRTerminate(t)
}
func TestHaPoolCdrs(t *testing.T) {
TestDmtAgentCdrs(t)
}
func TestHaPoolStopEngine(t *testing.T) {
TestDmtAgentStopEngine(t)
}
*/

View File

@@ -244,8 +244,8 @@ func metaValueExponent(m *diam.Message, argsTpl utils.RSRFields, roundingDecimal
return strconv.FormatFloat(utils.Round(res, roundingDecimals, utils.ROUNDING_MIDDLE), 'f', -1, 64), nil
}
func metaSum(m *diam.Message, argsTpl utils.RSRFields, roundingDecimals int) (string, error) {
valStr := composedFieldvalue(m, argsTpl, 0, nil)
func metaSum(m *diam.Message, argsTpl utils.RSRFields, passAtIndex, roundingDecimals int) (string, error) {
valStr := composedFieldvalue(m, argsTpl, passAtIndex, nil)
handlerArgs := strings.Split(valStr, utils.HandlerArgSep)
var summed float64
for _, arg := range handlerArgs {
@@ -287,6 +287,11 @@ func passesFieldFilter(m *diam.Message, fieldFilter *utils.RSRField, processorVa
if err != nil {
return false, 0
}
if len(avps) == 0 { // No AVP found in request, treat it same as empty
if fieldFilter.FilterPasses("") {
return true, -1
}
}
for avpIdx, avpVal := range avps { // First match wins due to index
if fieldFilter.FilterPasses(avpValAsString(avpVal)) {
return true, avpIdx
@@ -398,7 +403,7 @@ func fieldOutVal(m *diam.Message, cfgFld *config.CfgCdrField, extraParam interfa
case META_VALUE_EXPONENT:
outVal, err = metaValueExponent(m, cfgFld.Value, 10) // FixMe: add here configured number of decimals
case META_SUM:
outVal, err = metaSum(m, cfgFld.Value, 10)
outVal, err = metaSum(m, cfgFld.Value, passAtIndex, 10)
default:
outVal, err = metaHandler(m, cfgFld.HandlerId, cfgFld.Layout, extraParam.(time.Duration))
if err != nil {

View File

@@ -27,6 +27,7 @@ import (
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/sessionmanager"
"github.com/cgrates/cgrates/utils"
"github.com/fiorix/go-diameter/diam"
"github.com/fiorix/go-diameter/diam/avp"
@@ -133,12 +134,12 @@ func TestMetaSum(t *testing.T) {
}),
},
})
if val, err := metaSum(m, utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;^|;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", utils.INFIELD_SEP), 10); err != nil {
if val, err := metaSum(m, utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;^|;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", utils.INFIELD_SEP), 0, 10); err != nil {
t.Error(err)
} else if val != "9995" {
t.Error("Received: ", val)
}
if _, err = metaSum(m, utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", utils.INFIELD_SEP), 10); err == nil {
if _, err = metaSum(m, utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", utils.INFIELD_SEP), 0, 10); err == nil {
t.Error("Should have received error") // Insufficient number arguments
}
}
@@ -396,3 +397,86 @@ func TestCCASetProcessorAVPs(t *testing.T) {
t.Errorf("Expecting: %+v, received: %+v", eMessage, ccaMsg)
}
}
func TestCCRAsSMGenericEvent(t *testing.T) {
ccr := &CCR{ // Bare information, just the one needed for answer
SessionId: "ccrasgen1",
AuthApplicationId: 4,
CCRequestType: 3,
}
ccr.diamMessage = ccr.AsBareDiameterMessage()
ccr.diamMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
AVP: []*diam.AVP{
diam.NewAVP(446, avp.Mbit, 0, &diam.GroupedAVP{ // Used-Service-Unit
AVP: []*diam.AVP{
diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(17)), // CC-Time
diam.NewAVP(412, avp.Mbit, 0, datatype.Unsigned64(1341)), // CC-Input-Octets
diam.NewAVP(414, avp.Mbit, 0, datatype.Unsigned64(3079)), // CC-Output-Octets
},
}),
diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(99)),
},
})
ccr.diamMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
AVP: []*diam.AVP{
diam.NewAVP(446, avp.Mbit, 0, &diam.GroupedAVP{ // Used-Service-Unit
AVP: []*diam.AVP{
diam.NewAVP(452, avp.Mbit, 0, datatype.Enumerated(0)), // Tariff-Change-Usage
diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(20)), // CC-Time
diam.NewAVP(412, avp.Mbit, 0, datatype.Unsigned64(8046)), // CC-Input-Octets
diam.NewAVP(414, avp.Mbit, 0, datatype.Unsigned64(46193)), // CC-Output-Octets
},
}),
diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(1)),
},
})
ccr.diamMessage.NewAVP("FramedIPAddress", avp.Mbit, 0, datatype.OctetString("0AE40041"))
cfgFlds := make([]*config.CfgCdrField, 0)
eSMGEv := sessionmanager.SMGenericEvent{"EventName": "DIAMETER_CCR"}
if rSMGEv, err := ccr.AsSMGenericEvent(cfgFlds); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eSMGEv, rSMGEv) {
t.Errorf("Expecting: %+v, received: %+v", eSMGEv, rSMGEv)
}
cfgFlds = []*config.CfgCdrField{
&config.CfgCdrField{
Tag: "LastUsed",
FieldFilter: utils.ParseRSRFieldsMustCompile("~Multiple-Services-Credit-Control>Used-Service-Unit>CC-Input-Octets:s/^(.*)$/test/(test);Multiple-Services-Credit-Control>Rating-Group(1)", utils.INFIELD_SEP),
FieldId: "LastUsed",
Type: "*handler",
HandlerId: "*sum",
Value: utils.ParseRSRFieldsMustCompile("Multiple-Services-Credit-Control>Used-Service-Unit>CC-Input-Octets;^|;Multiple-Services-Credit-Control>Used-Service-Unit>CC-Output-Octets", utils.INFIELD_SEP),
Mandatory: true,
},
}
eSMGEv = sessionmanager.SMGenericEvent{"EventName": "DIAMETER_CCR", "LastUsed": "54239"}
if rSMGEv, err := ccr.AsSMGenericEvent(cfgFlds); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eSMGEv, rSMGEv) {
t.Errorf("Expecting: %+v, received: %+v", eSMGEv, rSMGEv)
}
cfgFlds = []*config.CfgCdrField{
&config.CfgCdrField{
Tag: "LastUsed",
FieldFilter: utils.ParseRSRFieldsMustCompile("~Multiple-Services-Credit-Control>Used-Service-Unit>CC-Input-Octets:s/^(.*)$/test/(test);Multiple-Services-Credit-Control>Rating-Group(99)", utils.INFIELD_SEP),
FieldId: "LastUsed",
Type: "*handler",
HandlerId: "*sum",
Value: utils.ParseRSRFieldsMustCompile("Multiple-Services-Credit-Control>Used-Service-Unit>CC-Input-Octets;^|;Multiple-Services-Credit-Control>Used-Service-Unit>CC-Output-Octets", utils.INFIELD_SEP),
Mandatory: true,
},
}
eSMGEv = sessionmanager.SMGenericEvent{"EventName": "DIAMETER_CCR", "LastUsed": "4420"}
if rSMGEv, err := ccr.AsSMGenericEvent(cfgFlds); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eSMGEv, rSMGEv) {
t.Errorf("Expecting: %+v, received: %+v", eSMGEv, rSMGEv)
}
}
func TestPassesFieldFilter(t *testing.T) {
m := diam.NewRequest(diam.CreditControl, 4, nil) // Multiple-Services-Credit-Control>Rating-Group
if pass, _ := passesFieldFilter(m, utils.ParseRSRFieldsMustCompile("Multiple-Services-Credit-Control>Rating-Group(^$)", utils.INFIELD_SEP)[0], nil); !pass {
t.Error("Does not pass")
}
}

View File

@@ -318,7 +318,7 @@ func (self *ApierV1) GetAccounts(attr utils.AttrGetAccounts, reply *[]interface{
var accountKeys []string
var err error
if len(attr.AccountIds) == 0 {
if accountKeys, err = self.AccountDb.GetKeysForPrefix(utils.ACCOUNT_PREFIX + attr.Tenant); err != nil {
if accountKeys, err = self.AccountDb.GetKeysForPrefix(utils.ACCOUNT_PREFIX+attr.Tenant, true); err != nil {
return err
}
} else {
@@ -426,7 +426,7 @@ func (self *ApierV1) modifyBalance(aType string, attr *AttrAddBalance, reply *st
Uuid: attr.BalanceUuid,
ID: attr.BalanceId,
Type: utils.StringPointer(attr.BalanceType),
Value: utils.Float64Pointer(attr.Value),
Value: &utils.ValueFormula{Static: attr.Value},
ExpirationDate: expTime,
RatingSubject: attr.RatingSubject,
Weight: attr.Weight,
@@ -514,7 +514,6 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error {
Uuid: attr.BalanceUUID,
ID: attr.BalanceID,
Type: utils.StringPointer(attr.BalanceType),
Value: attr.Value,
ExpirationDate: expTime,
RatingSubject: attr.RatingSubject,
Weight: attr.Weight,
@@ -522,6 +521,9 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error {
Disabled: attr.Disabled,
},
}
if attr.Value != nil {
a.Balance.Value = &utils.ValueFormula{Static: *attr.Value}
}
if attr.Directions != nil {
a.Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(*attr.Directions))
}
@@ -567,12 +569,11 @@ func (self *ApierV1) RemoveBalances(attr *AttrSetBalance, reply *string) error {
at := &engine.ActionTiming{}
at.SetAccountIDs(utils.StringMap{accID: true})
a := &engine.Action{
ActionType: engine.SET_BALANCE,
ActionType: engine.REMOVE_BALANCE,
Balance: &engine.BalanceFilter{
Uuid: attr.BalanceUUID,
ID: attr.BalanceID,
Type: utils.StringPointer(attr.BalanceType),
Value: attr.Value,
ExpirationDate: expTime,
RatingSubject: attr.RatingSubject,
Weight: attr.Weight,
@@ -580,6 +581,9 @@ func (self *ApierV1) RemoveBalances(attr *AttrSetBalance, reply *string) error {
Disabled: attr.Disabled,
},
}
if attr.Value != nil {
a.Balance.Value = &utils.ValueFormula{Static: *attr.Value}
}
if attr.Directions != nil {
a.Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(*attr.Directions))
}

View File

@@ -20,6 +20,7 @@ package v1
import (
"errors"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
@@ -51,7 +52,7 @@ func (self *ApierV1) AddRatingSubjectAliases(attrs AttrAddRatingSubjectAliases,
als := engine.Alias{Direction: utils.META_OUT, Tenant: attrs.Tenant, Category: attrs.Category, Account: alias, Subject: alias, Context: utils.ALIAS_CONTEXT_RATING,
Values: engine.AliasValues{&engine.AliasValue{DestinationId: utils.META_ANY,
Pairs: engine.AliasPairs{"Account": map[string]string{alias: attrs.Subject}, "Subject": map[string]string{alias: attrs.Subject}}, Weight: 10.0}}}
if err := aliases.SetAlias(als, &ignr); err != nil {
if err := aliases.Call("AliasesV1.SetAlias", &engine.AttrAddAlias{Alias: &als}, &ignr); err != nil {
return utils.NewErrServerError(err)
}
}
@@ -69,7 +70,7 @@ func (self *ApierV1) RemRatingSubjectAliases(tenantRatingSubject engine.TenantRa
return errors.New("ALIASES_NOT_ENABLED")
}
var reverseAliases map[string][]*engine.Alias
if err := aliases.GetReverseAlias(engine.AttrReverseAlias{Target: "Subject", Alias: tenantRatingSubject.Subject, Context: utils.ALIAS_CONTEXT_RATING}, &reverseAliases); err != nil {
if err := aliases.Call("AliasesV1.GetReverseAlias", &engine.AttrReverseAlias{Target: "Subject", Alias: tenantRatingSubject.Subject, Context: utils.ALIAS_CONTEXT_RATING}, &reverseAliases); err != nil {
return utils.NewErrServerError(err)
}
var ignr string
@@ -78,7 +79,7 @@ func (self *ApierV1) RemRatingSubjectAliases(tenantRatingSubject engine.TenantRa
if alias.Tenant != tenantRatingSubject.Tenant {
continue // From another tenant
}
if err := aliases.RemoveAlias(*alias, &ignr); err != nil {
if err := aliases.Call("AliasesV1.RemoveAlias", alias, &ignr); err != nil {
return utils.NewErrServerError(err)
}
}
@@ -103,7 +104,7 @@ func (self *ApierV1) AddAccountAliases(attrs AttrAddAccountAliases, reply *strin
als := engine.Alias{Direction: utils.META_OUT, Tenant: attrs.Tenant, Category: attrs.Category, Account: alias, Subject: alias, Context: utils.ALIAS_CONTEXT_RATING,
Values: engine.AliasValues{&engine.AliasValue{DestinationId: utils.META_ANY,
Pairs: engine.AliasPairs{"Account": map[string]string{alias: attrs.Account}, "Subject": map[string]string{alias: attrs.Account}}, Weight: 10.0}}}
if err := aliases.SetAlias(als, &ignr); err != nil {
if err := aliases.Call("AliasesV1.SetAlias", &engine.AttrAddAlias{Alias: &als}, &ignr); err != nil {
return utils.NewErrServerError(err)
}
}
@@ -121,7 +122,7 @@ func (self *ApierV1) RemAccountAliases(tenantAccount engine.TenantAccount, reply
return errors.New("ALIASES_NOT_ENABLED")
}
var reverseAliases map[string][]*engine.Alias
if err := aliases.GetReverseAlias(engine.AttrReverseAlias{Target: "Account", Alias: tenantAccount.Account, Context: utils.ALIAS_CONTEXT_RATING}, &reverseAliases); err != nil {
if err := aliases.Call("AliasesV1.GetReverseAlias", &engine.AttrReverseAlias{Target: "Account", Alias: tenantAccount.Account, Context: utils.ALIAS_CONTEXT_RATING}, &reverseAliases); err != nil {
return utils.NewErrServerError(err)
}
var ignr string
@@ -130,7 +131,7 @@ func (self *ApierV1) RemAccountAliases(tenantAccount engine.TenantAccount, reply
if alias.Tenant != tenantAccount.Tenant {
continue // From another tenant
}
if err := aliases.RemoveAlias(*alias, &ignr); err != nil {
if err := aliases.Call("AliasesV1.RemoveAlias", alias, &ignr); err != nil {
return utils.NewErrServerError(err)
}
}

View File

@@ -33,6 +33,7 @@ import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/scheduler"
"github.com/cgrates/cgrates/utils"
"github.com/cgrates/rpcclient"
)
const (
@@ -48,8 +49,9 @@ type ApierV1 struct {
Sched *scheduler.Scheduler
Config *config.CGRConfig
Responder *engine.Responder
CdrStatsSrv engine.StatsInterface
Users engine.UserService
CdrStatsSrv rpcclient.RpcClientConnection
Users rpcclient.RpcClientConnection
CDRs rpcclient.RpcClientConnection // FixMe: populate it from cgr-engine
}
func (self *ApierV1) GetDestination(dstId string, reply *engine.Destination) error {
@@ -103,11 +105,12 @@ func (self *ApierV1) GetRatingPlan(rplnId string, reply *engine.RatingPlan) erro
}
func (self *ApierV1) ExecuteAction(attr *utils.AttrExecuteAction, reply *string) error {
accID := utils.AccountKey(attr.Tenant, attr.Account)
at := &engine.ActionTiming{
ActionsID: attr.ActionsId,
}
at.SetAccountIDs(utils.StringMap{accID: true})
if attr.Tenant != "" && attr.Account != "" {
at.SetAccountIDs(utils.StringMap{utils.AccountKey(attr.Tenant, attr.Account): true})
}
if err := at.Execute(); err != nil {
*reply = err.Error()
return err
@@ -375,13 +378,13 @@ func (self *ApierV1) LoadTariffPlanFromStorDb(attrs AttrLoadTpFromStorDb, reply
}
if len(cstKeys) != 0 && self.CdrStatsSrv != nil {
if err := self.CdrStatsSrv.ReloadQueues(cstKeys, nil); err != nil {
if err := self.CdrStatsSrv.Call("CDRStatsV1.ReloadQueues", cstKeys, nil); err != nil {
return err
}
}
if len(userKeys) != 0 && self.Users != nil {
var r string
if err := self.Users.ReloadUsers("", &r); err != nil {
if err := self.Users.Call("AliasV1.ReloadUsers", "", &r); err != nil {
return err
}
}
@@ -502,10 +505,10 @@ func (self *ApierV1) SetActions(attrs utils.AttrSetActions, reply *string) error
}
storeActions := make(engine.Actions, len(attrs.Actions))
for idx, apiAct := range attrs.Actions {
var units *float64
var vf *utils.ValueFormula
if apiAct.Units != "" {
if x, err := strconv.ParseFloat(apiAct.Units, 64); err == nil {
units = &x
if x, err := utils.ParseBalanceFilterValue(apiAct.Units); err == nil {
vf = x
} else {
return err
}
@@ -521,17 +524,17 @@ func (self *ApierV1) SetActions(attrs utils.AttrSetActions, reply *string) error
}
a := &engine.Action{
Id: utils.GenUUID(),
Id: attrs.ActionsId,
ActionType: apiAct.Identifier,
Weight: apiAct.Weight,
ExpirationString: apiAct.ExpiryTime,
ExtraParameters: apiAct.ExtraParameters,
Filter: apiAct.Filter,
Balance: &engine.BalanceFilter{ // TODO: update this part
Uuid: utils.StringPointer(utils.GenUUID()),
Uuid: utils.StringPointer(apiAct.BalanceUuid),
ID: utils.StringPointer(apiAct.BalanceId),
Type: utils.StringPointer(apiAct.BalanceType),
Value: units,
Value: vf,
Weight: weight,
Directions: utils.StringMapPointer(utils.ParseStringMap(apiAct.Directions)),
DestinationIDs: utils.StringMapPointer(utils.ParseStringMap(apiAct.DestinationIds)),
@@ -560,7 +563,8 @@ func (self *ApierV1) GetActions(actsId string, reply *[]*utils.TPAction) error {
return utils.NewErrServerError(err)
}
for _, engAct := range engActs {
act := &utils.TPAction{Identifier: engAct.ActionType,
act := &utils.TPAction{
Identifier: engAct.ActionType,
ExpiryTime: engAct.ExpirationString,
ExtraParameters: engAct.ExtraParameters,
Filter: engAct.Filter,
@@ -808,23 +812,23 @@ func (self *ApierV1) GetCacheStats(attrs utils.AttrCacheStats, reply *utils.Cach
cs.DerivedChargers = cache2go.CountEntries(utils.DERIVEDCHARGERS_PREFIX)
cs.LcrProfiles = cache2go.CountEntries(utils.LCR_PREFIX)
cs.Aliases = cache2go.CountEntries(utils.ALIASES_PREFIX)
if self.CdrStatsSrv != nil && self.Config.CDRStatsEnabled {
if self.CdrStatsSrv != nil {
var queueIds []string
if err := self.CdrStatsSrv.GetQueueIds(0, &queueIds); err != nil {
if err := self.CdrStatsSrv.Call("CDRStatsV1.GetQueueIds", 0, &queueIds); err != nil {
return utils.NewErrServerError(err)
}
cs.CdrStats = len(queueIds)
}
if self.Config.RaterUserServer == utils.INTERNAL {
if self.Users != nil {
var ups engine.UserProfiles
if err := self.Users.GetUsers(engine.UserProfile{}, &ups); err != nil {
if err := self.Users.Call("UsersV1.GetUsers", &engine.UserProfile{}, &ups); err != nil {
return utils.NewErrServerError(err)
}
cs.Users = len(ups)
}
if loadHistInsts, err := self.AccountDb.GetLoadHistory(1, false); err != nil || len(loadHistInsts) == 0 {
if err != nil { // Not really an error here since we only count in cache
utils.Logger.Err(fmt.Sprintf("ApierV1.GetCacheStats, error on GetLoadHistory: %s", err.Error()))
utils.Logger.Warning(fmt.Sprintf("ApierV1.GetCacheStats, error on GetLoadHistory: %s", err.Error()))
}
cs.LastLoadId = utils.NOT_AVAILABLE
cs.LastLoadTime = utils.NOT_AVAILABLE
@@ -960,13 +964,14 @@ func (self *ApierV1) LoadTariffPlanFromFolder(attrs utils.AttrLoadTpFromFolder,
self.Sched.Reload(true)
}
if len(cstKeys) != 0 && self.CdrStatsSrv != nil {
if err := self.CdrStatsSrv.ReloadQueues(cstKeys, nil); err != nil {
var out int
if err := self.CdrStatsSrv.Call("CDRStatsV1.ReloadQueues", cstKeys, &out); err != nil {
return err
}
}
if len(userKeys) != 0 && self.Users != nil {
var r string
if err := self.Users.ReloadUsers("", &r); err != nil {
if err := self.Users.Call("UsersV1.ReloadUsers", "", &r); err != nil {
return err
}
}
@@ -1060,3 +1065,66 @@ func (self *ApierV1) GetLoadHistory(attrs utils.Paginator, reply *[]*engine.Load
*reply = loadHist[offset:nrItems]
return nil
}
type AttrRemActions struct {
ActionIDs []string
}
func (self *ApierV1) RemActions(attr AttrRemActions, reply *string) error {
if attr.ActionIDs == nil {
err := utils.ErrNotFound
*reply = err.Error()
return err
}
// The check could lead to very long execution time. So we decided to leave it at the user's risck.'
/*
stringMap := utils.NewStringMap(attr.ActionIDs...)
keys, err := self.RatingDb.GetKeysForPrefix(utils.ACTION_TRIGGER_PREFIX, true)
if err != nil {
*reply = err.Error()
return err
}
for _, key := range keys {
getAttrs, err := self.RatingDb.GetActionTriggers(key[len(utils.ACTION_TRIGGER_PREFIX):])
if err != nil {
*reply = err.Error()
return err
}
for _, atr := range getAttrs {
if _, found := stringMap[atr.ActionsID]; found {
// found action trigger referencing action; abort
err := fmt.Errorf("action %s refenced by action trigger %s", atr.ActionsID, atr.ID)
*reply = err.Error()
return err
}
}
}
allAplsMap, err := self.RatingDb.GetAllActionPlans()
if err != nil && err != utils.ErrNotFound {
*reply = err.Error()
return err
}
for _, apl := range allAplsMap {
for _, atm := range apl.ActionTimings {
if _, found := stringMap[atm.ActionsID]; found {
err := fmt.Errorf("action %s refenced by action plan %s", atm.ActionsID, apl.Id)
*reply = err.Error()
return err
}
}
}
*/
for _, aID := range attr.ActionIDs {
if err := self.RatingDb.RemoveActions(aID); err != nil {
*reply = err.Error()
return err
}
}
if err := self.RatingDb.CacheRatingPrefixes(utils.ACTION_PREFIX); err != nil {
*reply = err.Error()
return err
}
*reply = utils.OK
return nil
}

View File

@@ -1274,7 +1274,7 @@ func TestApierLoadTariffPlanFromFolder(t *testing.T) {
} else if reply != "OK" {
t.Error("Calling ApierV1.LoadTariffPlanFromFolder got reply: ", reply)
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond)
time.Sleep(time.Duration(2**waitRater) * time.Millisecond)
}
func TestApierResetDataAfterLoadFromFolder(t *testing.T) {
@@ -1294,11 +1294,11 @@ func TestApierResetDataAfterLoadFromFolder(t *testing.T) {
if err := rater.Call("ApierV1.GetCacheStats", args, &rcvStats); err != nil {
t.Error("Got error on ApierV1.GetCacheStats: ", err.Error())
} else {
if rcvStats.Destinations != 4 ||
rcvStats.RatingPlans != 3 ||
rcvStats.RatingProfiles != 3 ||
rcvStats.Actions != 6 ||
rcvStats.DerivedChargers != 2 {
if rcvStats.Destinations != 5 ||
rcvStats.RatingPlans != 5 ||
rcvStats.RatingProfiles != 5 ||
rcvStats.Actions != 11 ||
rcvStats.DerivedChargers != 3 {
t.Errorf("Calling ApierV1.GetCacheStats received: %+v", rcvStats)
}
}
@@ -1359,7 +1359,7 @@ func TestApierGetCallCostLog(t *testing.T) {
}
attrs.CgrId = "dummyid"
attrs.RunId = "default"
if err := rater.Call("ApierV1.GetCallCostLog", attrs, &cc); err == nil || err.Error() != "SERVER_ERROR: record not found" {
if err := rater.Call("ApierV1.GetCallCostLog", attrs, &cc); err == nil || err.Error() != utils.ErrNotFound.Error() {
t.Error("ApierV1.GetCallCostLog: should return NOT_FOUND, got:", err)
}
}

View File

@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package v1
import (
"errors"
"fmt"
"github.com/cgrates/cgrates/engine"
@@ -26,19 +27,19 @@ import (
)
// Retrieves the callCost out of CGR logDb
func (apier *ApierV1) GetCallCostLog(attrs utils.AttrGetCallCost, reply *engine.CallCost) error {
func (apier *ApierV1) GetCallCostLog(attrs utils.AttrGetCallCost, reply *engine.SMCost) error {
if attrs.CgrId == "" {
return utils.NewErrMandatoryIeMissing("CgrId")
}
if attrs.RunId == "" {
attrs.RunId = utils.META_DEFAULT
}
if cc, err := apier.CdrDb.GetCallCostLog(attrs.CgrId, attrs.RunId); err != nil {
if smcs, err := apier.CdrDb.GetSMCosts(attrs.CgrId, attrs.RunId, "", ""); err != nil {
return utils.NewErrServerError(err)
} else if cc == nil {
} else if len(smcs) == 0 {
return utils.ErrNotFound
} else {
*reply = *cc
*reply = *smcs[0]
}
return nil
}
@@ -85,3 +86,11 @@ func (apier *ApierV1) RemoveCDRs(attrs utils.RPCCDRsFilter, reply *string) error
*reply = "OK"
return nil
}
// New way of (re-)rating CDRs
func (apier *ApierV1) RateCDRs(attrs utils.AttrRateCDRs, reply *string) error {
if apier.CDRs == nil {
return errors.New("CDRS_NOT_ENABLED")
}
return apier.CDRs.Call("CDRsV1.RateCDRs", attrs, reply)
}

View File

@@ -23,11 +23,12 @@ import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
"github.com/cgrates/rpcclient"
)
// Interact with Stats server
type CDRStatsV1 struct {
CdrStats engine.StatsInterface
CdrStats rpcclient.RpcClientConnection
}
type AttrGetMetrics struct {
@@ -38,31 +39,32 @@ func (sts *CDRStatsV1) GetMetrics(attr AttrGetMetrics, reply *map[string]float64
if len(attr.StatsQueueId) == 0 {
return fmt.Errorf("%s:StatsQueueId", utils.ErrMandatoryIeMissing.Error())
}
return sts.CdrStats.GetValues(attr.StatsQueueId, reply)
return sts.CdrStats.Call("CDRStatsV1.GetValues", attr.StatsQueueId, reply)
}
func (sts *CDRStatsV1) GetQueueIds(empty string, reply *[]string) error {
return sts.CdrStats.GetQueueIds(0, reply)
return sts.CdrStats.Call("CDRStatsV1.GetQueueIds", 0, reply)
}
func (sts *CDRStatsV1) GetQueue(id string, sq *engine.StatsQueue) error {
return sts.CdrStats.GetQueue(id, sq)
return sts.CdrStats.Call("CDRStatsV1.GetQueue", id, sq)
}
func (sts *CDRStatsV1) AddQueue(cs *engine.CdrStats, reply *int) error {
return sts.CdrStats.AddQueue(cs, reply)
return sts.CdrStats.Call("CDRStatsV1.AddQueue", cs, reply)
}
func (sts *CDRStatsV1) RemoveQueue(qID string, reply *int) error {
return sts.CdrStats.RemoveQueue(qID, reply)
return sts.CdrStats.Call("CDRStatsV1.RemoveQueue", qID, reply)
}
func (sts *CDRStatsV1) GetQueueTriggers(id string, ats *engine.ActionTriggers) error {
return sts.CdrStats.GetQueueTriggers(id, ats)
return sts.CdrStats.Call("CDRStatsV1.GetQueueTriggers", id, ats)
}
func (sts *CDRStatsV1) ReloadQueues(attr utils.AttrCDRStatsReloadQueues, reply *string) error {
if err := sts.CdrStats.ReloadQueues(attr.StatsQueueIds, nil); err != nil {
var out int
if err := sts.CdrStats.Call("CDRStatsV1.ReloadQueues", attr.StatsQueueIds, &out); err != nil {
return err
}
*reply = utils.OK
@@ -70,7 +72,8 @@ func (sts *CDRStatsV1) ReloadQueues(attr utils.AttrCDRStatsReloadQueues, reply *
}
func (sts *CDRStatsV1) ResetQueues(attr utils.AttrCDRStatsReloadQueues, reply *string) error {
if err := sts.CdrStats.ResetQueues(attr.StatsQueueIds, nil); err != nil {
var out int
if err := sts.CdrStats.Call("CDRStatsV1.ResetQueues", attr.StatsQueueIds, &out); err != nil {
return err
}
*reply = utils.OK

View File

@@ -104,6 +104,13 @@ func TestCDRStatsLclGetQueueIds2(t *testing.T) {
} else if len(eQueueIds) != len(queueIds) {
t.Errorf("Expecting: %v, received: %v", eQueueIds, queueIds)
}
var rcvMetrics map[string]float64
expectedMetrics := map[string]float64{"ASR": -1, "ACD": -1}
if err := cdrstRpc.Call("CDRStatsV1.GetMetrics", AttrGetMetrics{StatsQueueId: "CDRST4"}, &rcvMetrics); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(expectedMetrics, rcvMetrics) {
t.Errorf("Expecting: %v, received: %v", expectedMetrics, rcvMetrics)
}
}
func TestCDRStatsLclPostCdrs(t *testing.T) {
@@ -112,28 +119,28 @@ func TestCDRStatsLclPostCdrs(t *testing.T) {
}
httpClient := new(http.Client)
storedCdrs := []*engine.CDR{
&engine.CDR{CGRID: utils.Sha1("dsafdsafa", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf",
&engine.CDR{CGRID: utils.Sha1("dsafdsafa", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsafa",
OriginHost: "192.168.1.1", Source: "test",
RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1001", Subject: "1001", Destination: "+4986517174963", SetupTime: time.Now(),
AnswerTime: time.Now(), RunID: utils.DEFAULT_RUNID, Usage: time.Duration(10) * time.Second,
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
},
&engine.CDR{CGRID: utils.Sha1("dsafdsafb", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf",
&engine.CDR{CGRID: utils.Sha1("dsafdsafb", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsafb",
OriginHost: "192.168.1.1", Source: "test",
RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1001", Subject: "1001", Destination: "+4986517174963", SetupTime: time.Now(),
AnswerTime: time.Now(), RunID: utils.DEFAULT_RUNID,
Usage: time.Duration(5) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
},
&engine.CDR{CGRID: utils.Sha1("dsafdsafc", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf",
&engine.CDR{CGRID: utils.Sha1("dsafdsafc", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsafc",
OriginHost: "192.168.1.1", Source: "test",
RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1001", Subject: "1001", Destination: "+4986517174963", SetupTime: time.Now(), AnswerTime: time.Now(),
RunID: utils.DEFAULT_RUNID,
Usage: time.Duration(30) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
},
&engine.CDR{CGRID: utils.Sha1("dsafdsafd", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf",
&engine.CDR{CGRID: utils.Sha1("dsafdsafd", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsafd",
OriginHost: "192.168.1.1", Source: "test",
RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1001", Subject: "1001", Destination: "+4986517174963", SetupTime: time.Now(), AnswerTime: time.Time{},
@@ -147,7 +154,6 @@ func TestCDRStatsLclPostCdrs(t *testing.T) {
}
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond)
}
func TestCDRStatsLclGetMetrics1(t *testing.T) {
@@ -205,3 +211,12 @@ func TestCDRStatsLclResetMetrics(t *testing.T) {
t.Errorf("Expecting: %v, received: %v", expectedMetrics2, rcvMetrics2)
}
}
func TestCDRStatsLclKillEngine(t *testing.T) {
if !*testLocal {
return
}
if err := engine.KillEngine(*waitRater); err != nil {
t.Error(err)
}
}

View File

@@ -30,7 +30,7 @@ type CdrsV1 struct {
// Designed for CGR internal usage
func (self *CdrsV1) ProcessCdr(cdr *engine.CDR, reply *string) error {
if err := self.CdrSrv.ProcessCdr(cdr); err != nil {
if err := self.CdrSrv.LocalProcessCdr(cdr); err != nil {
return utils.NewErrServerError(err)
}
*reply = utils.OK
@@ -46,7 +46,7 @@ func (self *CdrsV1) ProcessExternalCdr(cdr *engine.ExternalCDR, reply *string) e
return nil
}
// Remotely start mediation with specific runid, runs asynchronously, it's status will be displayed in syslog
// Remotely (re)rating, deprecated
func (self *CdrsV1) RateCdrs(attrs utils.AttrRateCdrs, reply *string) error {
cdrsFltr, err := attrs.AsCDRsFilter(self.CdrSrv.Timezone())
if err != nil {
@@ -59,10 +59,6 @@ func (self *CdrsV1) RateCdrs(attrs utils.AttrRateCdrs, reply *string) error {
return nil
}
func (self *CdrsV1) LogCallCost(ccl *engine.CallCostLog, reply *string) error {
if err := self.CdrSrv.LogCallCost(ccl); err != nil {
return utils.NewErrServerError(err)
}
*reply = utils.OK
return nil
func (self *CdrsV1) StoreSMCost(attr engine.AttrCDRSStoreSMCost, reply *string) error {
return self.CdrSrv.V1StoreSMCost(attr, reply)
}

View File

@@ -97,6 +97,9 @@ func (self *SMGenericV1) ActiveSessions(attrs utils.AttrSMGGetActiveSessions, re
if attrs.ToR != nil && *attrs.ToR != as.TOR {
continue
}
if attrs.OriginID != nil && *attrs.OriginID != as.OriginID {
continue
}
if attrs.RunID != nil && *attrs.RunID != as.RunId {
continue
}

View File

@@ -116,7 +116,7 @@ func TestSMGV1CacheStats(t *testing.T) {
}
var rcvStats *utils.CacheStats
expectedStats := &utils.CacheStats{Destinations: 4, RatingPlans: 4, RatingProfiles: 9, Actions: 7, ActionPlans: 4, SharedGroups: 1, Aliases: 1,
expectedStats := &utils.CacheStats{Destinations: 4, RatingPlans: 4, RatingProfiles: 9, Actions: 8, ActionPlans: 4, SharedGroups: 1, Aliases: 1,
DerivedChargers: 1, LcrProfiles: 5, CdrStats: 6, Users: 3, LastLoadId: smgV1LoadInst.LoadId, LastLoadTime: smgV1LoadInst.LoadTime.Format(time.RFC3339)}
var args utils.AttrCacheStats
if err := smgV1Rpc.Call("ApierV2.GetCacheStats", args, &rcvStats); err != nil {

View File

@@ -129,7 +129,15 @@ func (self *ApierV1) RemoveAccountActionTriggers(attr AttrRemoveAccountActionTri
return nil
}
func (self *ApierV1) ResetAccountActionTriggers(attr AttrRemoveAccountActionTriggers, reply *string) error {
type AttrResetAccountActionTriggers struct {
Tenant string
Account string
GroupID string
UniqueID string
Executed bool
}
func (self *ApierV1) ResetAccountActionTriggers(attr AttrResetAccountActionTriggers, reply *string) error {
if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
@@ -146,11 +154,13 @@ func (self *ApierV1) ResetAccountActionTriggers(attr AttrRemoveAccountActionTrig
if (attr.UniqueID == "" || at.UniqueID == attr.UniqueID) &&
(attr.GroupID == "" || at.ID == attr.GroupID) {
// reset action trigger
at.Executed = false
at.Executed = attr.Executed
}
}
account.ExecuteActionTriggers(nil)
if attr.Executed == false {
account.ExecuteActionTriggers(nil)
}
if err := self.AccountDb.SetAccount(account); err != nil {
return 0, err
}
@@ -175,7 +185,7 @@ type AttrSetAccountActionTriggers struct {
MinSleep *string
ExpirationDate *string
ActivationDate *string
BalanceId *string
BalanceID *string
BalanceType *string
BalanceDirections *[]string
BalanceDestinationIds *[]string
@@ -188,7 +198,7 @@ type AttrSetAccountActionTriggers struct {
BalanceBlocker *bool
BalanceDisabled *bool
MinQueuedItems *int
ActionsId *string
ActionsID *string
}
func (self *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers, reply *string) error {
@@ -238,8 +248,9 @@ func (self *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers,
}
at.ActivationDate = actTime
}
if attr.BalanceId != nil {
at.Balance.ID = attr.BalanceId
at.Balance = &engine.BalanceFilter{}
if attr.BalanceID != nil {
at.Balance.ID = attr.BalanceID
}
if attr.BalanceType != nil {
at.Balance.Type = attr.BalanceType
@@ -281,8 +292,8 @@ func (self *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers,
if attr.MinQueuedItems != nil {
at.MinQueuedItems = *attr.MinQueuedItems
}
if attr.ActionsId != nil {
at.ActionsID = *attr.ActionsId
if attr.ActionsID != nil {
at.ActionsID = *attr.ActionsID
}
}
@@ -300,3 +311,180 @@ func (self *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers,
*reply = utils.OK
return nil
}
type AttrSetActionTrigger struct {
GroupID string
UniqueID string
ThresholdType *string
ThresholdValue *float64
Recurrent *bool
MinSleep *string
ExpirationDate *string
ActivationDate *string
BalanceID *string
BalanceType *string
BalanceDirections *[]string
BalanceDestinationIds *[]string
BalanceWeight *float64
BalanceExpirationDate *string
BalanceTimingTags *[]string
BalanceRatingSubject *string
BalanceCategories *[]string
BalanceSharedGroups *[]string
BalanceBlocker *bool
BalanceDisabled *bool
MinQueuedItems *int
ActionsID *string
}
func (self *ApierV1) SetActionTrigger(attr AttrSetActionTrigger, reply *string) error {
if missing := utils.MissingStructFields(&attr, []string{"GroupID"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
atrs, _ := self.RatingDb.GetActionTriggers(attr.GroupID)
var newAtr *engine.ActionTrigger
if attr.UniqueID != "" {
//search for exiting one
for _, atr := range atrs {
if atr.UniqueID == attr.UniqueID {
newAtr = atr
break
}
}
}
if newAtr == nil {
newAtr = &engine.ActionTrigger{}
atrs = append(atrs, newAtr)
}
newAtr.ID = attr.GroupID
if attr.UniqueID != "" {
newAtr.UniqueID = attr.UniqueID
} else {
newAtr.UniqueID = utils.GenUUID()
}
if attr.ThresholdType != nil {
newAtr.ThresholdType = *attr.ThresholdType
}
if attr.ThresholdValue != nil {
newAtr.ThresholdValue = *attr.ThresholdValue
}
if attr.Recurrent != nil {
newAtr.Recurrent = *attr.Recurrent
}
if attr.MinSleep != nil {
minSleep, err := utils.ParseDurationWithSecs(*attr.MinSleep)
if err != nil {
*reply = err.Error()
return err
}
newAtr.MinSleep = minSleep
}
if attr.ExpirationDate != nil {
expTime, err := utils.ParseTimeDetectLayout(*attr.ExpirationDate, self.Config.DefaultTimezone)
if err != nil {
*reply = err.Error()
return err
}
newAtr.ExpirationDate = expTime
}
if attr.ActivationDate != nil {
actTime, err := utils.ParseTimeDetectLayout(*attr.ActivationDate, self.Config.DefaultTimezone)
if err != nil {
*reply = err.Error()
return err
}
newAtr.ActivationDate = actTime
}
newAtr.Balance = &engine.BalanceFilter{}
if attr.BalanceID != nil {
newAtr.Balance.ID = attr.BalanceID
}
if attr.BalanceType != nil {
newAtr.Balance.Type = attr.BalanceType
}
if attr.BalanceDirections != nil {
newAtr.Balance.Directions = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDirections...))
}
if attr.BalanceDestinationIds != nil {
newAtr.Balance.DestinationIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDestinationIds...))
}
if attr.BalanceWeight != nil {
newAtr.Balance.Weight = attr.BalanceWeight
}
if attr.BalanceExpirationDate != nil {
balanceExpTime, err := utils.ParseDate(*attr.BalanceExpirationDate)
if err != nil {
*reply = err.Error()
return err
}
newAtr.Balance.ExpirationDate = &balanceExpTime
}
if attr.BalanceTimingTags != nil {
newAtr.Balance.TimingIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceTimingTags...))
}
if attr.BalanceRatingSubject != nil {
newAtr.Balance.RatingSubject = attr.BalanceRatingSubject
}
if attr.BalanceCategories != nil {
newAtr.Balance.Categories = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceCategories...))
}
if attr.BalanceSharedGroups != nil {
newAtr.Balance.SharedGroups = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceSharedGroups...))
}
if attr.BalanceBlocker != nil {
newAtr.Balance.Blocker = attr.BalanceBlocker
}
if attr.BalanceDisabled != nil {
newAtr.Balance.Disabled = attr.BalanceDisabled
}
if attr.MinQueuedItems != nil {
newAtr.MinQueuedItems = *attr.MinQueuedItems
}
if attr.ActionsID != nil {
newAtr.ActionsID = *attr.ActionsID
}
if err := self.RatingDb.SetActionTriggers(attr.GroupID, atrs); err != nil {
*reply = err.Error()
return err
}
//no cache for action triggers
*reply = utils.OK
return nil
}
type AttrGetActionTriggers struct {
GroupIDs []string
}
func (self *ApierV1) GetActionTriggers(attr AttrGetActionTriggers, atrs *engine.ActionTriggers) error {
var allAttrs engine.ActionTriggers
if len(attr.GroupIDs) > 0 {
for _, key := range attr.GroupIDs {
getAttrs, err := self.RatingDb.GetActionTriggers(key)
if err != nil {
return err
}
allAttrs = append(allAttrs, getAttrs...)
}
} else {
keys, err := self.RatingDb.GetKeysForPrefix(utils.ACTION_TRIGGER_PREFIX, true)
if err != nil {
return err
}
for _, key := range keys {
getAttrs, err := self.RatingDb.GetActionTriggers(key[len(utils.ACTION_TRIGGER_PREFIX):])
if err != nil {
return err
}
allAttrs = append(allAttrs, getAttrs...)
}
}
*atrs = allAttrs
return nil
}

View File

@@ -33,7 +33,7 @@ func (self *ApierV2) GetAccounts(attr utils.AttrGetAccounts, reply *[]*engine.Ac
var accountKeys []string
var err error
if len(attr.AccountIds) == 0 {
if accountKeys, err = self.AccountDb.GetKeysForPrefix(utils.ACCOUNT_PREFIX + utils.ConcatenatedKey(attr.Tenant)); err != nil {
if accountKeys, err = self.AccountDb.GetKeysForPrefix(utils.ACCOUNT_PREFIX+attr.Tenant, true); err != nil {
return err
}
} else {

View File

@@ -21,6 +21,7 @@ package v2
import (
"errors"
"fmt"
"math"
"os"
"path"
"strings"
@@ -220,14 +221,12 @@ func (self *ApierV2) LoadTariffPlanFromFolder(attrs utils.AttrLoadTpFromFolder,
dcsKeys[idx] = utils.DERIVEDCHARGERS_PREFIX + dc
}
aps, _ := loader.GetLoadedIds(utils.ACTION_PLAN_PREFIX)
utils.Logger.Info("ApierV1.LoadTariffPlanFromFolder, reloading cache.")
utils.Logger.Info("ApierV2.LoadTariffPlanFromFolder, reloading cache.")
cstKeys, _ := loader.GetLoadedIds(utils.CDR_STATS_PREFIX)
userKeys, _ := loader.GetLoadedIds(utils.USERS_PREFIX)
li := loader.GetLoadInstance()
// release the tp data
loader.Init()
loader.Init() // release the tp data
if err := self.RatingDb.CacheRatingPrefixValues(map[string][]string{
utils.DESTINATION_PREFIX: dstKeys,
@@ -247,21 +246,76 @@ func (self *ApierV2) LoadTariffPlanFromFolder(attrs utils.AttrLoadTpFromFolder,
return err
}
if len(aps) != 0 && self.Sched != nil {
utils.Logger.Info("ApierV1.LoadTariffPlanFromFolder, reloading scheduler.")
utils.Logger.Info("ApierV2.LoadTariffPlanFromFolder, reloading scheduler.")
self.Sched.Reload(true)
}
if len(cstKeys) != 0 && self.CdrStatsSrv != nil {
if err := self.CdrStatsSrv.ReloadQueues(cstKeys, nil); err != nil {
var out int
if err := self.CdrStatsSrv.Call("CDRStatsV1.ReloadQueues", cstKeys, &out); err != nil {
return err
}
}
if len(userKeys) != 0 && self.Users != nil {
var r string
if err := self.Users.ReloadUsers("", &r); err != nil {
if err := self.Users.Call("UsersV1.ReloadUsers", "", &r); err != nil {
return err
}
}
*reply = *li
return nil
}
type AttrGetActions struct {
ActionIDs []string
Offset int // Set the item offset
Limit int // Limit number of items retrieved
}
// Retrieves actions attached to specific ActionsId within cache
func (self *ApierV2) GetActions(attr AttrGetActions, reply *map[string]engine.Actions) error {
var actionKeys []string
var err error
if len(attr.ActionIDs) == 0 {
if actionKeys, err = self.AccountDb.GetKeysForPrefix(utils.ACTION_PREFIX, false); err != nil {
return err
}
} else {
for _, accID := range attr.ActionIDs {
if len(accID) == 0 { // Source of error returned from redis (key not found)
continue
}
actionKeys = append(actionKeys, utils.ACCOUNT_PREFIX+accID)
}
}
if len(actionKeys) == 0 {
return nil
}
if attr.Offset > len(actionKeys) {
attr.Offset = len(actionKeys)
}
if attr.Offset < 0 {
attr.Offset = 0
}
var limitedActions []string
if attr.Limit != 0 {
max := math.Min(float64(attr.Offset+attr.Limit), float64(len(actionKeys)))
limitedActions = actionKeys[attr.Offset:int(max)]
} else {
limitedActions = actionKeys[attr.Offset:]
}
retActions := make(map[string]engine.Actions)
for _, accKey := range limitedActions {
key := accKey[len(utils.ACTION_PREFIX):]
acts, err := self.RatingDb.GetActions(key, false)
if err != nil {
return utils.NewErrServerError(err)
}
if len(acts) > 0 {
retActions[key] = acts
}
}
*reply = retActions
return nil
}

View File

@@ -247,6 +247,7 @@ func TestV2CDRsMySQLRateWithoutTP(t *testing.T) {
if !*testLocal {
return
}
//"d32a571d7bcbc6700fd35c1c0c5c6f458a62e260"
rawCdrCGRID := utils.Sha1("bbb1", time.Date(2015, 11, 21, 10, 47, 24, 0, time.UTC).String())
// Rate the injected CDR, should not rate it since we have no TP loaded
attrs := utils.AttrRateCdrs{CgrIds: []string{rawCdrCGRID}}

View File

@@ -18,7 +18,7 @@ type CacheItem struct {
type ResponseCache struct {
ttl time.Duration
cache map[string]*CacheItem
semaphore map[string]chan bool
semaphore map[string]chan bool // used for waiting till the first goroutine processes the response
mu sync.RWMutex
}
@@ -32,6 +32,7 @@ func NewResponseCache(ttl time.Duration) *ResponseCache {
}
func (rc *ResponseCache) Cache(key string, item *CacheItem) {
//utils.Logger.Info("key: " + key)
if rc.ttl == 0 {
return
}
@@ -54,13 +55,21 @@ func (rc *ResponseCache) Get(key string) (*CacheItem, error) {
if rc.ttl == 0 {
return nil, utils.ErrNotImplemented
}
rc.mu.RLock()
item, ok := rc.cache[key]
rc.mu.RUnlock()
if ok {
//utils.Logger.Info(",,,,,,,,,,,,,,,,,,,,,Found key: " + key)
return item, nil
}
rc.wait(key) // wait for other goroutine processsing this key
rc.mu.RLock()
defer rc.mu.RUnlock()
item, ok := rc.cache[key]
item, ok = rc.cache[key]
if !ok {
return nil, ErrNotFound
}
//utils.Logger.Info("............................Found key: " + key)
return item, nil
}

View File

@@ -32,6 +32,7 @@ import (
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
"github.com/cgrates/rpcclient"
"gopkg.in/fsnotify.v1"
)
@@ -54,7 +55,7 @@ Common parameters within configs processed:
Parameters specific per config instance:
* duMultiplyFactor, cdrSourceId, cdrFilter, cdrFields
*/
func NewCdrc(cdrcCfgs map[string]*config.CdrcConfig, httpSkipTlsCheck bool, cdrs engine.Connector, closeChan chan struct{}, dfltTimezone string) (*Cdrc, error) {
func NewCdrc(cdrcCfgs []*config.CdrcConfig, httpSkipTlsCheck bool, cdrs rpcclient.RpcClientConnection, closeChan chan struct{}, dfltTimezone string) (*Cdrc, error) {
var cdrcCfg *config.CdrcConfig
for _, cdrcCfg = range cdrcCfgs { // Take the first config out, does not matter which one
break
@@ -82,10 +83,10 @@ func NewCdrc(cdrcCfgs map[string]*config.CdrcConfig, httpSkipTlsCheck bool, cdrs
type Cdrc struct {
httpSkipTlsCheck bool
cdrcCfgs map[string]*config.CdrcConfig // All cdrc config profiles attached to this CDRC (key will be profile instance name)
cdrcCfgs []*config.CdrcConfig // All cdrc config profiles attached to this CDRC (key will be profile instance name)
dfltCdrcCfg *config.CdrcConfig
timezone string
cdrs engine.Connector
cdrs rpcclient.RpcClientConnection
httpClient *http.Client
closeChan chan struct{} // Used to signal config reloads when we need to span different CDRC-Client
maxOpenFiles chan struct{} // Maximum number of simultaneous files processed
@@ -201,7 +202,7 @@ func (self *Cdrc) processFile(filePath string) error {
utils.Logger.Info(fmt.Sprintf("<Cdrc> DryRun CDR: %+v", storedCdr))
continue
}
if err := self.cdrs.ProcessCdr(storedCdr, &reply); err != nil {
if err := self.cdrs.Call("Responder.ProcessCdr", storedCdr, &reply); err != nil {
utils.Logger.Err(fmt.Sprintf("<Cdrc> Failed sending CDR, %+v, error: %s", storedCdr, err.Error()))
} else if reply != "OK" {
utils.Logger.Err(fmt.Sprintf("<Cdrc> Received unexpected reply for CDR, %+v, reply: %s", storedCdr, reply))

View File

@@ -1,219 +0,0 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2012-2015 ITsysCOM
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 cdrc
import (
"errors"
"flag"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
"testing"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
/*
README:
Enable local tests by passing '-local' to the go test command
It is expected that the data folder of CGRateS exists at path /usr/share/cgrates/data or passed via command arguments.
Prior running the tests, create database and users by running:
mysql -pyourrootpwd < /usr/share/cgrates/data/storage/mysql/create_db_with_users.sql
What these tests do:
* Flush tables in storDb.
* Start engine with default configuration and give it some time to listen (here caching can slow down).
*
*/
var cfgPath string
var cfg *config.CGRConfig
var cdrcCfgs map[string]*config.CdrcConfig
var cdrcCfg *config.CdrcConfig
var testLocal = flag.Bool("local", false, "Perform the tests only on local test environment, not by default.") // This flag will be passed here via "go test -local" args
var dataDir = flag.String("data_dir", "/usr/share/cgrates", "CGR data dir path here")
var storDbType = flag.String("stordb_type", "mysql", "The type of the storDb database <mysql>")
var waitRater = flag.Int("wait_rater", 300, "Number of miliseconds to wait for rater to start and cache")
var fileContent1 = `accid11,prepaid,out,cgrates.org,call,1001,1001,+4986517174963,2013-02-03 19:54:00,62,supplier1,172.16.1.1
accid12,prepaid,out,cgrates.org,call,1001,1001,+4986517174963,2013-02-03 19:54:00,62,supplier1,172.16.1.1
dummy_data
accid13,prepaid,out,cgrates.org,call,1001,1001,+4986517174963,2013-02-03 19:54:00,62,supplier1,172.16.1.1
`
var fileContent2 = `accid21,prepaid,out,cgrates.org,call,1001,1001,+4986517174963,2013-02-03 19:54:00,62,supplier1,172.16.1.1
accid22,prepaid,out,cgrates.org,call,1001,1001,+4986517174963,2013-02-03 19:54:00,62,supplier1,172.16.1.1
#accid1,prepaid,out,cgrates.org,call,1001,1001,+4986517174963,2013-02-03 19:54:00,62,supplier1,172.16.1.1
accid23,prepaid,out,cgrates.org,call,1001,1001,+4986517174963,2013-02-03 19:54:00,62,supplier1,172.16.1.1`
var fileContent3 = `accid31;prepaid;out;cgrates.org;call;1001;1001;+4986517174963;2013-02-03 19:54:00;62;supplier1;172.16.1.1
accid32;prepaid;out;cgrates.org;call;1001;1001;+4986517174963;2013-02-03 19:54:00;62;supplier1;172.16.1.1
#accid1;prepaid;out;cgrates.org;call;1001;1001;+4986517174963;2013-02-03 19:54:00;62;supplier1;172.16.1.1
accid33;prepaid;out;cgrates.org;call;1001;1001;+4986517174963;2013-02-03 19:54:00;62;supplier1;172.16.1.1`
func startEngine() error {
enginePath, err := exec.LookPath("cgr-engine")
if err != nil {
return errors.New("Cannot find cgr-engine executable")
}
stopEngine()
engine := exec.Command(enginePath, "-config", cfgPath)
if err := engine.Start(); err != nil {
return fmt.Errorf("Cannot start cgr-engine: %s", err.Error())
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time to rater to fire up
return nil
}
func stopEngine() error {
exec.Command("pkill", "cgr-engine").Run() // Just to make sure another one is not running, bit brutal maybe we can fine tune it
return nil
}
// Need it here and not in init since Travis has no possibility to load local file
func TestCsvLclLoadConfigt(*testing.T) {
if !*testLocal {
return
}
cfgPath = path.Join(*dataDir, "conf", "samples", "apier")
cfg, _ = config.NewCGRConfigFromFolder(cfgPath)
if len(cfg.CdrcProfiles) > 0 {
cdrcCfgs = cfg.CdrcProfiles["/var/log/cgrates/cdrc/in"]
}
}
func TestCsvLclEmptyTables(t *testing.T) {
if !*testLocal {
return
}
if *storDbType != utils.MYSQL {
t.Fatal("Unsupported storDbType")
}
mysql, err := engine.NewMySQLStorage(cfg.StorDBHost, cfg.StorDBPort, cfg.StorDBName, cfg.StorDBUser, cfg.StorDBPass, cfg.StorDBMaxOpenConns, cfg.StorDBMaxIdleConns)
if err != nil {
t.Fatal("Error on opening database connection: ", err)
}
for _, scriptName := range []string{utils.CREATE_CDRS_TABLES_SQL, utils.CREATE_TARIFFPLAN_TABLES_SQL} {
if err := mysql.CreateTablesFromScript(path.Join(*dataDir, "storage", *storDbType, scriptName)); err != nil {
t.Fatal("Error on mysql creation: ", err.Error())
return // No point in going further
}
}
if _, err := mysql.Db.Query(fmt.Sprintf("SELECT 1 from %s", utils.TBL_CDRS)); err != nil {
t.Fatal(err.Error())
}
}
// Creates cdr files and starts the engine
func TestCsvLclCreateCdrFiles(t *testing.T) {
if !*testLocal {
return
}
if cdrcCfgs == nil {
t.Fatal("Empty default cdrc configuration")
}
for _, cdrcCfg = range cdrcCfgs { // Take the first config out, does not matter which one
break
}
if err := os.RemoveAll(cdrcCfg.CdrInDir); err != nil {
t.Fatal("Error removing folder: ", cdrcCfg.CdrInDir, err)
}
if err := os.MkdirAll(cdrcCfg.CdrInDir, 0755); err != nil {
t.Fatal("Error creating folder: ", cdrcCfg.CdrInDir, err)
}
if err := os.RemoveAll(cdrcCfg.CdrOutDir); err != nil {
t.Fatal("Error removing folder: ", cdrcCfg.CdrOutDir, err)
}
if err := os.MkdirAll(cdrcCfg.CdrOutDir, 0755); err != nil {
t.Fatal("Error creating folder: ", cdrcCfg.CdrOutDir, err)
}
if err := ioutil.WriteFile(path.Join(cdrcCfg.CdrInDir, "file1.csv"), []byte(fileContent1), 0644); err != nil {
t.Fatal(err.Error)
}
if err := ioutil.WriteFile(path.Join(cdrcCfg.CdrInDir, "file2.csv"), []byte(fileContent2), 0644); err != nil {
t.Fatal(err.Error)
}
}
func TestCsvLclProcessCdrDir(t *testing.T) {
if !*testLocal {
return
}
var cdrcCfg *config.CdrcConfig
for _, cdrcCfg = range cdrcCfgs { // Take the first config out, does not matter which one
break
}
if cdrcCfg.Cdrs == utils.INTERNAL { // For now we only test over network
cdrcCfg.Cdrs = "127.0.0.1:2013"
}
if err := startEngine(); err != nil {
t.Fatal(err.Error())
}
cdrc, err := NewCdrc(cdrcCfgs, true, nil, make(chan struct{}), "")
if err != nil {
t.Fatal(err.Error())
}
if err := cdrc.processCdrDir(); err != nil {
t.Error(err)
}
stopEngine()
}
// Creates cdr files and starts the engine
func TestCsvLclCreateCdr3File(t *testing.T) {
if !*testLocal {
return
}
if err := os.RemoveAll(cdrcCfg.CdrInDir); err != nil {
t.Fatal("Error removing folder: ", cdrcCfg.CdrInDir, err)
}
if err := os.MkdirAll(cdrcCfg.CdrInDir, 0755); err != nil {
t.Fatal("Error creating folder: ", cdrcCfg.CdrInDir, err)
}
if err := ioutil.WriteFile(path.Join(cdrcCfg.CdrInDir, "file3.csv"), []byte(fileContent3), 0644); err != nil {
t.Fatal(err.Error)
}
}
func TestCsvLclProcessCdr3Dir(t *testing.T) {
if !*testLocal {
return
}
if cdrcCfg.Cdrs == utils.INTERNAL { // For now we only test over network
cdrcCfg.Cdrs = "127.0.0.1:2013"
}
if err := startEngine(); err != nil {
t.Fatal(err.Error())
}
cdrc, err := NewCdrc(cdrcCfgs, true, nil, make(chan struct{}), "")
if err != nil {
t.Fatal(err.Error())
}
if err := cdrc.processCdrDir(); err != nil {
t.Error(err)
}
stopEngine()
}

View File

@@ -156,7 +156,7 @@ BYE|3111f3c9|49ca4c42|a58ebaae40d08d6757d8424fb09c4c54@0:0:0:0:0:0:0:0|200|OK|14
}}
cdrc := &Cdrc{CdrFormat: utils.OSIPS_FLATSTORE, cdrSourceIds: []string{"TEST_CDRC"}, failedCallsPrefix: "missed_calls",
cdrFields: cdrFields, partialRecords: make(map[string]map[string]*PartialFlatstoreRecord),
guard: engine.NewGuardianLock()}
guard: engine.Guardian}
cdrsContent := bytes.NewReader([]byte(flatstoreCdrs))
csvReader := csv.NewReader(cdrsContent)
csvReader.Comma = '|'
@@ -283,7 +283,7 @@ INVITE|324cb497|d4af7023|8deaadf2ae9a17809a391f05af31afb0@0:0:0:0:0:0:0:0|486|Bu
}}
cdrc := &Cdrc{CdrFormat: utils.OSIPS_FLATSTORE, cdrSourceIds: []string{"TEST_CDRC"}, failedCallsPrefix: "missed_calls",
cdrFields: cdrFields, partialRecords: make(map[string]map[string]*PartialFlatstoreRecord),
guard: engine.NewGuardianLock()}
guard: engine.Guardian}
cdrsContent := bytes.NewReader([]byte(flatstoreCdrs))
csvReader := csv.NewReader(cdrsContent)
csvReader.Comma = '|'

View File

@@ -20,6 +20,7 @@ package cdrc
import (
"encoding/csv"
"encoding/json"
"errors"
"fmt"
"os"
@@ -93,7 +94,7 @@ func pairToRecord(part1, part2 *PartialFlatstoreRecord) ([]string, error) {
func NewPartialRecordsCache(ttl time.Duration, cdrOutDir string, csvSep rune) (*PartialRecordsCache, error) {
return &PartialRecordsCache{ttl: ttl, cdrOutDir: cdrOutDir, csvSep: csvSep,
partialRecords: make(map[string]map[string]*PartialFlatstoreRecord), guard: engine.NewGuardianLock()}, nil
partialRecords: make(map[string]map[string]*PartialFlatstoreRecord), guard: engine.Guardian}, nil
}
type PartialRecordsCache struct {
@@ -180,7 +181,7 @@ func (self *PartialRecordsCache) UncachePartial(fileName string, pr *PartialFlat
}
func NewCsvRecordsProcessor(csvReader *csv.Reader, timezone, fileName string,
dfltCdrcCfg *config.CdrcConfig, cdrcCfgs map[string]*config.CdrcConfig,
dfltCdrcCfg *config.CdrcConfig, cdrcCfgs []*config.CdrcConfig,
httpSkipTlsCheck bool, partialRecordsCache *PartialRecordsCache) *CsvRecordsProcessor {
return &CsvRecordsProcessor{csvReader: csvReader, timezone: timezone, fileName: fileName,
dfltCdrcCfg: dfltCdrcCfg, cdrcCfgs: cdrcCfgs,
@@ -193,7 +194,7 @@ type CsvRecordsProcessor struct {
timezone string // Timezone for CDRs which are not clearly specifying it
fileName string
dfltCdrcCfg *config.CdrcConfig
cdrcCfgs map[string]*config.CdrcConfig
cdrcCfgs []*config.CdrcConfig
processedRecordsNr int64 // Number of content records in file
httpSkipTlsCheck bool
partialRecordsCache *PartialRecordsCache // Shared by cdrc so we can cache for all files in a folder
@@ -246,8 +247,8 @@ func (self *CsvRecordsProcessor) processPartialRecord(record []string) ([]string
// Takes the record from a slice and turns it into StoredCdrs, posting them to the cdrServer
func (self *CsvRecordsProcessor) processRecord(record []string) ([]*engine.CDR, error) {
recordCdrs := make([]*engine.CDR, 0) // More CDRs based on the number of filters and field templates
for cdrcId, cdrcCfg := range self.cdrcCfgs { // cdrFields coming from more templates will produce individual storCdr records
recordCdrs := make([]*engine.CDR, 0) // More CDRs based on the number of filters and field templates
for _, cdrcCfg := range self.cdrcCfgs { // cdrFields coming from more templates will produce individual storCdr records
// Make sure filters are matching
filterBreak := false
for _, rsrFilter := range cdrcCfg.CdrFilter {
@@ -264,12 +265,12 @@ func (self *CsvRecordsProcessor) processRecord(record []string) ([]*engine.CDR,
if filterBreak { // Stop importing cdrc fields profile due to non matching filter
continue
}
if storedCdr, err := self.recordToStoredCdr(record, cdrcId); err != nil {
if storedCdr, err := self.recordToStoredCdr(record, cdrcCfg); err != nil {
return nil, fmt.Errorf("Failed converting to StoredCdr, error: %s", err.Error())
} else {
recordCdrs = append(recordCdrs, storedCdr)
}
if !self.cdrcCfgs[cdrcId].ContinueOnSuccess {
if !cdrcCfg.ContinueOnSuccess {
break
}
}
@@ -277,11 +278,11 @@ func (self *CsvRecordsProcessor) processRecord(record []string) ([]*engine.CDR,
}
// Takes the record out of csv and turns it into storedCdr which can be processed by CDRS
func (self *CsvRecordsProcessor) recordToStoredCdr(record []string, cdrcId string) (*engine.CDR, error) {
storedCdr := &engine.CDR{OriginHost: "0.0.0.0", Source: self.cdrcCfgs[cdrcId].CdrSourceId, ExtraFields: make(map[string]string), Cost: -1}
func (self *CsvRecordsProcessor) recordToStoredCdr(record []string, cdrcCfg *config.CdrcConfig) (*engine.CDR, error) {
storedCdr := &engine.CDR{OriginHost: "0.0.0.0", Source: cdrcCfg.CdrSourceId, ExtraFields: make(map[string]string), Cost: -1}
var err error
var lazyHttpFields []*config.CfgCdrField
for _, cdrFldCfg := range self.cdrcCfgs[cdrcId].ContentFields {
for _, cdrFldCfg := range cdrcCfg.ContentFields {
if utils.IsSliceMember([]string{utils.KAM_FLATSTORE, utils.OSIPS_FLATSTORE}, self.dfltCdrcCfg.CdrFormat) { // Hardcode some values in case of flatstore
switch cdrFldCfg.FieldId {
case utils.ACCID:
@@ -314,8 +315,8 @@ func (self *CsvRecordsProcessor) recordToStoredCdr(record []string, cdrcId strin
}
}
storedCdr.CGRID = utils.Sha1(storedCdr.OriginID, storedCdr.SetupTime.UTC().String())
if storedCdr.ToR == utils.DATA && self.cdrcCfgs[cdrcId].DataUsageMultiplyFactor != 0 {
storedCdr.Usage = time.Duration(float64(storedCdr.Usage.Nanoseconds()) * self.cdrcCfgs[cdrcId].DataUsageMultiplyFactor)
if storedCdr.ToR == utils.DATA && cdrcCfg.DataUsageMultiplyFactor != 0 {
storedCdr.Usage = time.Duration(float64(storedCdr.Usage.Nanoseconds()) * cdrcCfg.DataUsageMultiplyFactor)
}
for _, httpFieldCfg := range lazyHttpFields { // Lazy process the http fields
var outValByte []byte
@@ -323,7 +324,12 @@ func (self *CsvRecordsProcessor) recordToStoredCdr(record []string, cdrcId strin
for _, rsrFld := range httpFieldCfg.Value {
httpAddr += rsrFld.ParseValue("")
}
if outValByte, err = utils.HttpJsonPost(httpAddr, self.httpSkipTlsCheck, storedCdr); err != nil && httpFieldCfg.Mandatory {
var jsn []byte
jsn, err = json.Marshal(storedCdr)
if err != nil {
return nil, err
}
if outValByte, err = utils.HttpJsonPost(httpAddr, self.httpSkipTlsCheck, jsn); err != nil && httpFieldCfg.Mandatory {
return nil, err
} else {
fieldVal = string(outValByte)

201
cdrc/csv_it_test.go Normal file
View File

@@ -0,0 +1,201 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2012-2015 ITsysCOM
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 cdrc
import (
"flag"
"io/ioutil"
"net/rpc"
"net/rpc/jsonrpc"
"os"
"path"
"testing"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
/*
README:
Enable local tests by passing '-local' to the go test command
It is expected that the data folder of CGRateS exists at path /usr/share/cgrates/data or passed via command arguments.
Prior running the tests, create database and users by running:
mysql -pyourrootpwd < /usr/share/cgrates/data/storage/mysql/create_db_with_users.sql
What these tests do:
* Flush tables in storDb.
* Start engine with default configuration and give it some time to listen (here caching can slow down).
*
*/
var csvCfgPath string
var csvCfg *config.CGRConfig
var cdrcCfgs []*config.CdrcConfig
var cdrcCfg *config.CdrcConfig
var cdrcRpc *rpc.Client
var testLocal = flag.Bool("local", false, "Perform the tests only on local test environment, not by default.") // This flag will be passed here via "go test -local" args
var testIT = flag.Bool("integration", false, "Perform the tests only on local test environment, not by default.") // This flag will be passed here via "go test -local" args
var dataDir = flag.String("data_dir", "/usr/share/cgrates", "CGR data dir path here")
var waitRater = flag.Int("wait_rater", 300, "Number of miliseconds to wait for rater to start and cache")
var fileContent1 = `dbafe9c8614c785a65aabd116dd3959c3c56f7f6,default,*voice,dsafdsaf,*rated,*out,cgrates.org,call,1001,1001,+4986517174963,2013-11-07 08:42:25 +0000 UTC,2013-11-07 08:42:26 +0000 UTC,10s,1.0100,val_extra3,"",val_extra1
dbafe9c8614c785a65aabd116dd3959c3c56f7f7,default,*voice,dsafdsag,*rated,*out,cgrates.org,call,1001,1001,+4986517174964,2013-11-07 09:42:25 +0000 UTC,2013-11-07 09:42:26 +0000 UTC,20s,1.0100,val_extra3,"",val_extra1
`
var fileContent2 = `accid21;*prepaid;itsyscom.com;1001;086517174963;2013-02-03 19:54:00;62;val_extra3;"";val_extra1
accid22;*postpaid;itsyscom.com;1001;+4986517174963;2013-02-03 19:54:00;123;val_extra3;"";val_extra1
#accid1;*pseudoprepaid;itsyscom.com;1001;+4986517174963;2013-02-03 19:54:00;12;val_extra3;"";val_extra1
accid23;*rated;cgrates.org;1001;086517174963;2013-02-03 19:54:00;26;val_extra3;"";val_extra1`
func TestCsvITInitConfig(t *testing.T) {
if !*testIT {
return
}
var err error
csvCfgPath = path.Join(*dataDir, "conf", "samples", "cdrccsv")
if csvCfg, err = config.NewCGRConfigFromFolder(csvCfgPath); err != nil {
t.Fatal("Got config error: ", err.Error())
}
}
// InitDb so we can rely on count
func TestCsvITInitCdrDb(t *testing.T) {
if !*testIT {
return
}
if err := engine.InitStorDb(csvCfg); err != nil {
t.Fatal(err)
}
}
func TestCsvITCreateCdrDirs(t *testing.T) {
if !*testIT {
return
}
for _, cdrcProfiles := range csvCfg.CdrcProfiles {
for _, cdrcInst := range cdrcProfiles {
for _, dir := range []string{cdrcInst.CdrInDir, cdrcInst.CdrOutDir} {
if err := os.RemoveAll(dir); err != nil {
t.Fatal("Error removing folder: ", dir, err)
}
if err := os.MkdirAll(dir, 0755); err != nil {
t.Fatal("Error creating folder: ", dir, err)
}
}
}
}
}
func TestCsvITStartEngine(t *testing.T) {
if !*testIT {
return
}
if _, err := engine.StopStartEngine(csvCfgPath, *waitRater); err != nil {
t.Fatal(err)
}
}
// Connect rpc client to rater
func TestCsvITRpcConn(t *testing.T) {
if !*testIT {
return
}
var err error
cdrcRpc, err = jsonrpc.Dial("tcp", csvCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
if err != nil {
t.Fatal("Could not connect to rater: ", err.Error())
}
}
// The default scenario, out of cdrc defined in .cfg file
func TestCsvITHandleCdr1File(t *testing.T) {
if !*testIT {
return
}
fileName := "file1.csv"
tmpFilePath := path.Join("/tmp", fileName)
if err := ioutil.WriteFile(tmpFilePath, []byte(fileContent1), 0644); err != nil {
t.Fatal(err.Error)
}
if err := os.Rename(tmpFilePath, path.Join("/tmp/cdrctests/csvit1/in", fileName)); err != nil {
t.Fatal("Error moving file to processing directory: ", err)
}
}
// Scenario out of first .xml config
func TestCsvITHandleCdr2File(t *testing.T) {
if !*testIT {
return
}
fileName := "file2.csv"
tmpFilePath := path.Join("/tmp", fileName)
if err := ioutil.WriteFile(tmpFilePath, []byte(fileContent2), 0644); err != nil {
t.Fatal(err.Error)
}
if err := os.Rename(tmpFilePath, path.Join("/tmp/cdrctests/csvit2/in", fileName)); err != nil {
t.Fatal("Error moving file to processing directory: ", err)
}
}
func TestCsvITProcessedFiles(t *testing.T) {
if !*testIT {
return
}
time.Sleep(time.Duration(2**waitRater) * time.Millisecond)
if outContent1, err := ioutil.ReadFile("/tmp/cdrctests/csvit1/out/file1.csv"); err != nil {
t.Error(err)
} else if fileContent1 != string(outContent1) {
t.Errorf("Expecting: %q, received: %q", fileContent1, string(outContent1))
}
if outContent2, err := ioutil.ReadFile("/tmp/cdrctests/csvit2/out/file2.csv"); err != nil {
t.Error(err)
} else if fileContent2 != string(outContent2) {
t.Errorf("Expecting: %q, received: %q", fileContent1, string(outContent2))
}
}
func TestCsvITAnalyseCDRs(t *testing.T) {
if !*testIT {
return
}
var reply []*engine.ExternalCDR
if err := cdrcRpc.Call("ApierV2.GetCdrs", utils.RPCCDRsFilter{}, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 6 { // 1 injected, 1 rated, 1 *raw and it's pair in *default run
t.Error("Unexpected number of CDRs returned: ", len(reply))
}
if err := cdrcRpc.Call("ApierV2.GetCdrs", utils.RPCCDRsFilter{DestinationPrefixes: []string{"08651"}}, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 0 { // Original 08651 was converted
t.Error("Unexpected number of CDRs returned: ", len(reply))
}
}
func TestCsvITKillEngine(t *testing.T) {
if !*testIT {
return
}
if err := engine.KillEngine(*waitRater); err != nil {
t.Error(err)
}
}

View File

@@ -30,21 +30,21 @@ import (
func TestCsvRecordForkCdr(t *testing.T) {
cgrConfig, _ := config.NewDefaultCGRConfig()
cdrcConfig := cgrConfig.CdrcProfiles["/var/log/cgrates/cdrc/in"][utils.META_DEFAULT]
cdrcConfig := cgrConfig.CdrcProfiles["/var/log/cgrates/cdrc/in"][0]
cdrcConfig.CdrSourceId = "TEST_CDRC"
cdrcConfig.ContentFields = append(cdrcConfig.ContentFields, &config.CfgCdrField{Tag: "SupplierTest", Type: utils.META_COMPOSED, FieldId: utils.SUPPLIER, Value: []*utils.RSRField{&utils.RSRField{Id: "14"}}})
cdrcConfig.ContentFields = append(cdrcConfig.ContentFields, &config.CfgCdrField{Tag: "DisconnectCauseTest", Type: utils.META_COMPOSED, FieldId: utils.DISCONNECT_CAUSE,
Value: []*utils.RSRField{&utils.RSRField{Id: "16"}}})
//
csvProcessor := &CsvRecordsProcessor{dfltCdrcCfg: cdrcConfig, cdrcCfgs: map[string]*config.CdrcConfig{"*default": cdrcConfig}}
csvProcessor := &CsvRecordsProcessor{dfltCdrcCfg: cdrcConfig, cdrcCfgs: []*config.CdrcConfig{cdrcConfig}}
cdrRow := []string{"firstField", "secondField"}
_, err := csvProcessor.recordToStoredCdr(cdrRow, "*default")
_, err := csvProcessor.recordToStoredCdr(cdrRow, cdrcConfig)
if err == nil {
t.Error("Failed to corectly detect missing fields from record")
}
cdrRow = []string{"ignored", "ignored", utils.VOICE, "acc1", utils.META_PREPAID, "*out", "cgrates.org", "call", "1001", "1001", "+4986517174963",
"2013-02-03 19:50:00", "2013-02-03 19:54:00", "62", "supplier1", "172.16.1.1", "NORMAL_DISCONNECT"}
rtCdr, err := csvProcessor.recordToStoredCdr(cdrRow, "*default")
rtCdr, err := csvProcessor.recordToStoredCdr(cdrRow, cdrcConfig)
if err != nil {
t.Error("Failed to parse CDR in rated cdr", err)
}
@@ -76,14 +76,14 @@ func TestCsvRecordForkCdr(t *testing.T) {
func TestCsvDataMultiplyFactor(t *testing.T) {
cgrConfig, _ := config.NewDefaultCGRConfig()
cdrcConfig := cgrConfig.CdrcProfiles["/var/log/cgrates/cdrc/in"][utils.META_DEFAULT]
cdrcConfig := cgrConfig.CdrcProfiles["/var/log/cgrates/cdrc/in"][0]
cdrcConfig.CdrSourceId = "TEST_CDRC"
cdrcConfig.ContentFields = []*config.CfgCdrField{&config.CfgCdrField{Tag: "TORField", Type: utils.META_COMPOSED, FieldId: utils.TOR, Value: []*utils.RSRField{&utils.RSRField{Id: "0"}}},
&config.CfgCdrField{Tag: "UsageField", Type: utils.META_COMPOSED, FieldId: utils.USAGE, Value: []*utils.RSRField{&utils.RSRField{Id: "1"}}}}
csvProcessor := &CsvRecordsProcessor{dfltCdrcCfg: cdrcConfig, cdrcCfgs: map[string]*config.CdrcConfig{"*default": cdrcConfig}}
csvProcessor.cdrcCfgs["*default"].DataUsageMultiplyFactor = 0
csvProcessor := &CsvRecordsProcessor{dfltCdrcCfg: cdrcConfig, cdrcCfgs: []*config.CdrcConfig{cdrcConfig}}
csvProcessor.cdrcCfgs[0].DataUsageMultiplyFactor = 0
cdrRow := []string{"*data", "1"}
rtCdr, err := csvProcessor.recordToStoredCdr(cdrRow, "*default")
rtCdr, err := csvProcessor.recordToStoredCdr(cdrRow, cdrcConfig)
if err != nil {
t.Error("Failed to parse CDR in rated cdr", err)
}
@@ -100,7 +100,7 @@ func TestCsvDataMultiplyFactor(t *testing.T) {
if !reflect.DeepEqual(expectedCdr, rtCdr) {
t.Errorf("Expected: \n%v, \nreceived: \n%v", expectedCdr, rtCdr)
}
csvProcessor.cdrcCfgs["*default"].DataUsageMultiplyFactor = 1024
csvProcessor.cdrcCfgs[0].DataUsageMultiplyFactor = 1024
expectedCdr = &engine.CDR{
CGRID: utils.Sha1("", sTime.String()),
ToR: cdrRow[0],
@@ -110,7 +110,7 @@ func TestCsvDataMultiplyFactor(t *testing.T) {
ExtraFields: map[string]string{},
Cost: -1,
}
if rtCdr, _ := csvProcessor.recordToStoredCdr(cdrRow, "*default"); !reflect.DeepEqual(expectedCdr, rtCdr) {
if rtCdr, _ := csvProcessor.recordToStoredCdr(cdrRow, cdrcConfig); !reflect.DeepEqual(expectedCdr, rtCdr) {
t.Errorf("Expected: \n%v, \nreceived: \n%v", expectedCdr, rtCdr)
}
cdrRow = []string{"*voice", "1"}
@@ -123,7 +123,7 @@ func TestCsvDataMultiplyFactor(t *testing.T) {
ExtraFields: map[string]string{},
Cost: -1,
}
if rtCdr, _ := csvProcessor.recordToStoredCdr(cdrRow, "*default"); !reflect.DeepEqual(expectedCdr, rtCdr) {
if rtCdr, _ := csvProcessor.recordToStoredCdr(cdrRow, cdrcConfig); !reflect.DeepEqual(expectedCdr, rtCdr) {
t.Errorf("Expected: \n%v, \nreceived: \n%v", expectedCdr, rtCdr)
}
}

View File

@@ -85,7 +85,11 @@ func TestFlatstoreLclCreateCdrFiles(t *testing.T) {
if flatstoreCfg == nil {
t.Fatal("Empty default cdrc configuration")
}
flatstoreCdrcCfg = flatstoreCfg.CdrcProfiles["/tmp/cgr_flatstore/cdrc/in"]["FLATSTORE"]
for _, cdrcCfg := range flatstoreCfg.CdrcProfiles["/tmp/cgr_flatstore/cdrc/in"] {
if cdrcCfg.ID == "FLATSTORE" {
flatstoreCdrcCfg = cdrcCfg
}
}
if err := os.RemoveAll(flatstoreCdrcCfg.CdrInDir); err != nil {
t.Fatal("Error removing folder: ", flatstoreCdrcCfg.CdrInDir, err)
}

View File

@@ -20,16 +20,18 @@ package cdrc
import (
"bufio"
"encoding/json"
"fmt"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
"io"
"net/http"
"os"
"strconv"
"strings"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
func fwvValue(cdrLine string, indexStart, width int, padding string) string {
@@ -47,14 +49,14 @@ func fwvValue(cdrLine string, indexStart, width int, padding string) string {
return rawVal
}
func NewFwvRecordsProcessor(file *os.File, dfltCfg *config.CdrcConfig, cdrcCfgs map[string]*config.CdrcConfig, httpClient *http.Client, httpSkipTlsCheck bool, timezone string) *FwvRecordsProcessor {
func NewFwvRecordsProcessor(file *os.File, dfltCfg *config.CdrcConfig, cdrcCfgs []*config.CdrcConfig, httpClient *http.Client, httpSkipTlsCheck bool, timezone string) *FwvRecordsProcessor {
return &FwvRecordsProcessor{file: file, cdrcCfgs: cdrcCfgs, dfltCfg: dfltCfg, httpSkipTlsCheck: httpSkipTlsCheck, timezone: timezone}
}
type FwvRecordsProcessor struct {
file *os.File
dfltCfg *config.CdrcConfig // General parameters
cdrcCfgs map[string]*config.CdrcConfig
cdrcCfgs []*config.CdrcConfig
httpClient *http.Client
httpSkipTlsCheck bool
timezone string
@@ -123,11 +125,11 @@ func (self *FwvRecordsProcessor) ProcessNextRecord() ([]*engine.CDR, error) {
}
self.processedRecordsNr += 1
record := string(buf)
for cfgKey, cdrcCfg := range self.cdrcCfgs {
if passes := self.recordPassesCfgFilter(record, cfgKey); !passes {
for _, cdrcCfg := range self.cdrcCfgs {
if passes := self.recordPassesCfgFilter(record, cdrcCfg); !passes {
continue
}
if storedCdr, err := self.recordToStoredCdr(record, cfgKey); err != nil {
if storedCdr, err := self.recordToStoredCdr(record, cdrcCfg, cdrcCfg.ID); err != nil {
return nil, fmt.Errorf("Failed converting to StoredCdr, error: %s", err.Error())
} else {
recordCdrs = append(recordCdrs, storedCdr)
@@ -139,9 +141,9 @@ func (self *FwvRecordsProcessor) ProcessNextRecord() ([]*engine.CDR, error) {
return recordCdrs, nil
}
func (self *FwvRecordsProcessor) recordPassesCfgFilter(record, configKey string) bool {
func (self *FwvRecordsProcessor) recordPassesCfgFilter(record string, cdrcCfg *config.CdrcConfig) bool {
filterPasses := true
for _, rsrFilter := range self.cdrcCfgs[configKey].CdrFilter {
for _, rsrFilter := range cdrcCfg.CdrFilter {
if rsrFilter == nil { // Nil filter does not need to match anything
continue
}
@@ -156,8 +158,8 @@ func (self *FwvRecordsProcessor) recordPassesCfgFilter(record, configKey string)
return filterPasses
}
// Converts a record (header or normal) to StoredCdr
func (self *FwvRecordsProcessor) recordToStoredCdr(record string, cfgKey string) (*engine.CDR, error) {
// Converts a record (header or normal) to CDR
func (self *FwvRecordsProcessor) recordToStoredCdr(record string, cdrcCfg *config.CdrcConfig, cfgKey string) (*engine.CDR, error) {
var err error
var lazyHttpFields []*config.CfgCdrField
var cfgFields []*config.CfgCdrField
@@ -169,13 +171,13 @@ func (self *FwvRecordsProcessor) recordToStoredCdr(record string, cfgKey string)
storedCdr = &engine.CDR{OriginHost: "0.0.0.0", ExtraFields: make(map[string]string), Cost: -1}
}
if cfgKey == "*header" {
cfgFields = self.dfltCfg.HeaderFields
storedCdr.Source = self.dfltCfg.CdrSourceId
duMultiplyFactor = self.dfltCfg.DataUsageMultiplyFactor
cfgFields = cdrcCfg.HeaderFields
storedCdr.Source = cdrcCfg.CdrSourceId
duMultiplyFactor = cdrcCfg.DataUsageMultiplyFactor
} else {
cfgFields = self.cdrcCfgs[cfgKey].ContentFields
storedCdr.Source = self.cdrcCfgs[cfgKey].CdrSourceId
duMultiplyFactor = self.cdrcCfgs[cfgKey].DataUsageMultiplyFactor
cfgFields = cdrcCfg.ContentFields
storedCdr.Source = cdrcCfg.CdrSourceId
duMultiplyFactor = cdrcCfg.DataUsageMultiplyFactor
}
for _, cdrFldCfg := range cfgFields {
var fieldVal string
@@ -214,7 +216,12 @@ func (self *FwvRecordsProcessor) recordToStoredCdr(record string, cfgKey string)
for _, rsrFld := range httpFieldCfg.Value {
httpAddr += rsrFld.ParseValue("")
}
if outValByte, err = utils.HttpJsonPost(httpAddr, self.httpSkipTlsCheck, storedCdr); err != nil && httpFieldCfg.Mandatory {
var jsn []byte
jsn, err = json.Marshal(storedCdr)
if err != nil {
return nil, err
}
if outValByte, err = utils.HttpJsonPost(httpAddr, self.httpSkipTlsCheck, jsn); err != nil && httpFieldCfg.Mandatory {
return nil, err
} else {
fieldVal = string(outValByte)
@@ -237,7 +244,7 @@ func (self *FwvRecordsProcessor) processHeader() error {
return fmt.Errorf("In header, line len: %d, have read: %d", self.lineLen, nRead)
}
var err error
if self.headerCdr, err = self.recordToStoredCdr(string(buf), "*header"); err != nil {
if self.headerCdr, err = self.recordToStoredCdr(string(buf), self.dfltCfg, "*header"); err != nil {
return err
}
return nil

View File

@@ -19,8 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package cdrc
import (
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"io/ioutil"
"net/rpc"
"net/rpc/jsonrpc"
@@ -28,6 +26,9 @@ import (
"path"
"testing"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
)
var fwvCfgPath string
@@ -91,7 +92,11 @@ func TestFwvLclCreateCdrFiles(t *testing.T) {
if fwvCfg == nil {
t.Fatal("Empty default cdrc configuration")
}
fwvCdrcCfg = fwvCfg.CdrcProfiles["/tmp/cgr_fwv/cdrc/in"]["FWV1"]
for _, cdrcCfg := range fwvCfg.CdrcProfiles["/tmp/cgr_fwv/cdrc/in"] {
if cdrcCfg.ID == "FWV1" {
fwvCdrcCfg = cdrcCfg
}
}
if err := os.RemoveAll(fwvCdrcCfg.CdrInDir); err != nil {
t.Fatal("Error removing folder: ", fwvCdrcCfg.CdrInDir, err)
}

View File

@@ -45,11 +45,11 @@ func TestFwvValue(t *testing.T) {
func TestFwvRecordPassesCfgFilter(t *testing.T) {
//record, configKey string) bool {
cgrConfig, _ := config.NewDefaultCGRConfig()
cdrcConfig := cgrConfig.CdrcProfiles["/var/log/cgrates/cdrc/in"][utils.META_DEFAULT] // We don't really care that is for .csv since all we want to test are the filters
cdrcConfig := cgrConfig.CdrcProfiles["/var/log/cgrates/cdrc/in"][0] // We don't really care that is for .csv since all we want to test are the filters
cdrcConfig.CdrFilter = utils.ParseRSRFieldsMustCompile(`~52:s/^0(\d{9})/+49${1}/(^+49123123120)`, utils.INFIELD_SEP)
fwvRp := &FwvRecordsProcessor{cdrcCfgs: cgrConfig.CdrcProfiles["/var/log/cgrates/cdrc/in"]}
cdrLine := "CDR0000010 0 20120708181506000123451234 0040123123120 004 000018009980010001ISDN ABC 10Buiten uw regio EHV 00000009190000000009"
if passesFilter := fwvRp.recordPassesCfgFilter(cdrLine, utils.META_DEFAULT); !passesFilter {
if passesFilter := fwvRp.recordPassesCfgFilter(cdrLine, cdrcConfig); !passesFilter {
t.Error("Not passes filter")
}
}

View File

@@ -116,13 +116,13 @@ type CdrExporter struct {
// Return Json marshaled callCost attached to
// Keep it separately so we test only this part in local tests
func (cdre *CdrExporter) getCdrCostDetails(CGRID, runId string) (string, error) {
cc, err := cdre.cdrDb.GetCallCostLog(CGRID, runId)
smcs, err := cdre.cdrDb.GetSMCosts(CGRID, runId, "", "")
if err != nil {
return "", err
} else if cc == nil {
} else if len(smcs) == 0 {
return "", nil
}
ccJson, _ := json.Marshal(cc)
ccJson, _ := json.Marshal(smcs[0].CostDetails)
return string(ccJson), nil
}
@@ -352,9 +352,13 @@ func (cdre *CdrExporter) processCdr(cdr *engine.CDR) error {
case utils.META_HTTP_POST:
var outValByte []byte
httpAddr := cfgFld.Value.Id()
jsn, err := json.Marshal(cdr)
if err != nil {
return err
}
if len(httpAddr) == 0 {
err = fmt.Errorf("Empty http address for field %s type %s", cfgFld.Tag, cfgFld.Type)
} else if outValByte, err = utils.HttpJsonPost(httpAddr, cdre.httpSkipTlsCheck, cdr); err == nil {
} else if outValByte, err = utils.HttpJsonPost(httpAddr, cdre.httpSkipTlsCheck, jsn); err == nil {
outVal = string(outValByte)
if len(outVal) == 0 && cfgFld.Mandatory {
err = fmt.Errorf("Empty result for http_post field: %s", cfgFld.Tag)

View File

@@ -22,8 +22,8 @@ import (
"flag"
"fmt"
"log"
// _ "net/http/pprof"
"os"
"reflect"
"runtime"
"runtime/pprof"
"strconv"
@@ -71,7 +71,7 @@ var (
err error
)
func startCdrcs(internalCdrSChan chan *engine.CdrServer, internalRaterChan chan *engine.Responder, exitChan chan bool) {
func startCdrcs(internalCdrSChan, internalRaterChan chan rpcclient.RpcClientConnection, exitChan chan bool) {
cdrcInitialized := false // Control whether the cdrc was already initialized (so we don't reload in that case)
var cdrcChildrenChan chan struct{} // Will use it to communicate with the children of one fork
for {
@@ -87,42 +87,34 @@ func startCdrcs(internalCdrSChan chan *engine.CdrServer, internalRaterChan chan
}
// Start CDRCs
for _, cdrcCfgs := range cfg.CdrcProfiles {
var cdrcCfg *config.CdrcConfig
for _, cdrcCfg = range cdrcCfgs { // Take a random config out since they should be the same
break
var enabledCfgs []*config.CdrcConfig
for _, cdrcCfg := range cdrcCfgs { // Take a random config out since they should be the same
if cdrcCfg.Enabled {
enabledCfgs = append(enabledCfgs, cdrcCfg)
}
}
if cdrcCfg.Enabled == false {
continue // Ignore not enabled
if len(enabledCfgs) != 0 {
go startCdrc(internalCdrSChan, internalRaterChan, cdrcCfgs, cfg.HttpSkipTlsVerify, cdrcChildrenChan, exitChan)
}
go startCdrc(internalCdrSChan, internalRaterChan, cdrcCfgs, cfg.HttpSkipTlsVerify, cdrcChildrenChan, exitChan)
}
cdrcInitialized = true // Initialized
}
}
// Fires up a cdrc instance
func startCdrc(internalCdrSChan chan *engine.CdrServer, internalRaterChan chan *engine.Responder, cdrcCfgs map[string]*config.CdrcConfig, httpSkipTlsCheck bool,
func startCdrc(internalCdrSChan, internalRaterChan chan rpcclient.RpcClientConnection, cdrcCfgs []*config.CdrcConfig, httpSkipTlsCheck bool,
closeChan chan struct{}, exitChan chan bool) {
var cdrsConn engine.Connector
var cdrcCfg *config.CdrcConfig
for _, cdrcCfg = range cdrcCfgs { // Take the first config out, does not matter which one
break
}
if cdrcCfg.Cdrs == utils.INTERNAL {
cdrsChan := <-internalCdrSChan // This will signal that the cdrs part is populated in internalRaterChan
internalCdrSChan <- cdrsChan // Put it back for other components
resp := <-internalRaterChan
cdrsConn = resp
internalRaterChan <- resp
} else {
conn, err := rpcclient.NewRpcClient("tcp", cdrcCfg.Cdrs, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<CDRC> Could not connect to CDRS via RPC: %v", err))
exitChan <- true
return
}
cdrsConn = &engine.RPCClientConnector{Client: conn}
cdrsConn, err := engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cdrcCfg.CdrsConns, internalCdrSChan, cfg.InternalTtl)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<CDRC> Could not connect to CDRS via RPC: %s", err.Error()))
exitChan <- true
return
}
cdrc, err := cdrc.NewCdrc(cdrcCfgs, httpSkipTlsCheck, cdrsConn, closeChan, cfg.DefaultTimezone)
if err != nil {
@@ -136,51 +128,31 @@ func startCdrc(internalCdrSChan chan *engine.CdrServer, internalRaterChan chan *
}
}
func startSmGeneric(internalSMGChan chan rpcclient.RpcClientConnection, internalRaterChan chan *engine.Responder, server *utils.Server, exitChan chan bool) {
utils.Logger.Info("Starting CGRateS SM-Generic service.")
var raterConn, cdrsConn engine.Connector
var client *rpcclient.RpcClient
var err error
// Connect to rater
for _, raterCfg := range cfg.SmGenericConfig.HaRater {
if raterCfg.Server == utils.INTERNAL {
resp := <-internalRaterChan
raterConn = resp // Will overwrite here for the sake of keeping internally the new configuration format for ha connections
internalRaterChan <- resp
} else {
client, err = rpcclient.NewRpcClient("tcp", raterCfg.Server, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil)
if err != nil { //Connected so no need to reiterate
utils.Logger.Crit(fmt.Sprintf("<SM-Generic> Could not connect to Rater via RPC: %v", err))
exitChan <- true
return
}
raterConn = &engine.RPCClientConnector{Client: client}
func startSmGeneric(internalSMGChan chan rpcclient.RpcClientConnection, internalRaterChan, internalCDRSChan chan rpcclient.RpcClientConnection, server *utils.Server, exitChan chan bool) {
utils.Logger.Info("Starting CGRateS SMGeneric service.")
var ralsConns, cdrsConn *rpcclient.RpcClientPool
if len(cfg.SmGenericConfig.RALsConns) != 0 {
ralsConns, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cfg.SmGenericConfig.RALsConns, internalRaterChan, cfg.InternalTtl)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<SMGeneric> Could not connect to RAL: %s", err.Error()))
exitChan <- true
return
}
}
// Connect to CDRS
if reflect.DeepEqual(cfg.SmGenericConfig.HaCdrs, cfg.SmGenericConfig.HaRater) {
cdrsConn = raterConn
} else if len(cfg.SmGenericConfig.HaCdrs) != 0 {
for _, cdrsCfg := range cfg.SmGenericConfig.HaCdrs {
if cdrsCfg.Server == utils.INTERNAL {
resp := <-internalRaterChan
cdrsConn = resp
internalRaterChan <- resp
} else {
client, err = rpcclient.NewRpcClient("tcp", cdrsCfg.Server, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<SM-Generic> Could not connect to CDRS via RPC: %v", err))
exitChan <- true
return
}
cdrsConn = &engine.RPCClientConnector{Client: client}
}
if len(cfg.SmGenericConfig.CDRsConns) != 0 {
cdrsConn, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cfg.SmGenericConfig.CDRsConns, internalCDRSChan, cfg.InternalTtl)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<SMGeneric> Could not connect to RAL: %s", err.Error()))
exitChan <- true
return
}
}
smg_econns := sessionmanager.NewSMGExternalConnections()
sm := sessionmanager.NewSMGeneric(cfg, raterConn, cdrsConn, cfg.DefaultTimezone, smg_econns)
sm := sessionmanager.NewSMGeneric(cfg, ralsConns, cdrsConn, cfg.DefaultTimezone, smg_econns)
if err = sm.Connect(); err != nil {
utils.Logger.Err(fmt.Sprintf("<SM-Generic> error: %s!", err))
utils.Logger.Err(fmt.Sprintf("<SMGeneric> error: %s!", err))
}
// Register RPC handler
smgRpc := v1.NewSMGenericV1(sm)
@@ -198,31 +170,24 @@ func startSmGeneric(internalSMGChan chan rpcclient.RpcClientConnection, internal
func startDiameterAgent(internalSMGChan, internalPubSubSChan chan rpcclient.RpcClientConnection, exitChan chan bool) {
utils.Logger.Info("Starting CGRateS DiameterAgent service.")
var smgConn, pubsubConn *rpcclient.RpcClient
var err error
if cfg.DiameterAgentCfg().SMGeneric == utils.INTERNAL {
smgRpc := <-internalSMGChan
internalSMGChan <- smgRpc
smgConn, err = rpcclient.NewRpcClient("", "", 0, 0, rpcclient.INTERNAL_RPC, smgRpc)
} else {
smgConn, err = rpcclient.NewRpcClient("tcp", cfg.DiameterAgentCfg().SMGeneric, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil)
var smgConn, pubsubConn *rpcclient.RpcClientPool
if len(cfg.DiameterAgentCfg().SMGenericConns) != 0 {
smgConn, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cfg.DiameterAgentCfg().SMGenericConns, internalSMGChan, cfg.InternalTtl)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<DiameterAgent> Could not connect to SMG: %s", err.Error()))
exitChan <- true
return
}
}
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<DiameterAgent> Could not connect to SMG: %s", err.Error()))
exitChan <- true
return
}
if cfg.DiameterAgentCfg().PubSubS == utils.INTERNAL {
pubSubRpc := <-internalPubSubSChan
internalPubSubSChan <- pubSubRpc
pubsubConn, err = rpcclient.NewRpcClient("", "", 0, 0, rpcclient.INTERNAL_RPC, pubSubRpc)
} else if len(cfg.DiameterAgentCfg().PubSubS) != 0 {
pubsubConn, err = rpcclient.NewRpcClient("tcp", cfg.DiameterAgentCfg().PubSubS, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil)
}
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<DiameterAgent> Could not connect to PubSubS: %s", err.Error()))
exitChan <- true
return
if len(cfg.DiameterAgentCfg().PubSubConns) != 0 {
pubsubConn, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cfg.DiameterAgentCfg().PubSubConns, internalPubSubSChan, cfg.InternalTtl)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<DiameterAgent> Could not connect to PubSubS: %s", err.Error()))
exitChan <- true
return
}
}
da, err := agents.NewDiameterAgent(cfg, smgConn, pubsubConn)
if err != nil {
@@ -236,146 +201,86 @@ func startDiameterAgent(internalSMGChan, internalPubSubSChan chan rpcclient.RpcC
exitChan <- true
}
func startSmFreeSWITCH(internalRaterChan chan *engine.Responder, cdrDb engine.CdrStorage, exitChan chan bool) {
utils.Logger.Info("Starting CGRateS SM-FreeSWITCH service.")
var raterConn, cdrsConn engine.Connector
var client *rpcclient.RpcClient
var err error
// Connect to rater
for _, raterCfg := range cfg.SmFsConfig.HaRater {
if raterCfg.Server == utils.INTERNAL {
resp := <-internalRaterChan
raterConn = resp // Will overwrite here for the sake of keeping internally the new configuration format for ha connections
internalRaterChan <- resp
} else {
client, err = rpcclient.NewRpcClient("tcp", raterCfg.Server, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil)
if err != nil { //Connected so no need to reiterate
utils.Logger.Crit(fmt.Sprintf("<SM-FreeSWITCH> Could not connect to rater via RPC: %v", err))
exitChan <- true
return
}
raterConn = &engine.RPCClientConnector{Client: client}
func startSmFreeSWITCH(internalRaterChan, internalCDRSChan chan rpcclient.RpcClientConnection, cdrDb engine.CdrStorage, exitChan chan bool) {
utils.Logger.Info("Starting CGRateS SMFreeSWITCH service.")
var ralsConn, cdrsConn *rpcclient.RpcClientPool
if len(cfg.SmFsConfig.RALsConns) != 0 {
ralsConn, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cfg.SmFsConfig.RALsConns, internalRaterChan, cfg.InternalTtl)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<SMFreeSWITCH> Could not connect to RAL: %s", err.Error()))
exitChan <- true
return
}
}
// Connect to CDRS
if reflect.DeepEqual(cfg.SmFsConfig.HaCdrs, cfg.SmFsConfig.HaRater) {
cdrsConn = raterConn
} else if len(cfg.SmFsConfig.HaCdrs) != 0 {
for _, cdrsCfg := range cfg.SmFsConfig.HaCdrs {
if cdrsCfg.Server == utils.INTERNAL {
resp := <-internalRaterChan
cdrsConn = resp
internalRaterChan <- resp
} else {
client, err = rpcclient.NewRpcClient("tcp", cdrsCfg.Server, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<SM-FreeSWITCH> Could not connect to CDRS via RPC: %v", err))
exitChan <- true
return
}
cdrsConn = &engine.RPCClientConnector{Client: client}
}
if len(cfg.SmFsConfig.CDRsConns) != 0 {
cdrsConn, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cfg.SmFsConfig.CDRsConns, internalCDRSChan, cfg.InternalTtl)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<SMFreeSWITCH> Could not connect to RAL: %s", err.Error()))
exitChan <- true
return
}
}
sm := sessionmanager.NewFSSessionManager(cfg.SmFsConfig, raterConn, cdrsConn, cfg.DefaultTimezone)
sm := sessionmanager.NewFSSessionManager(cfg.SmFsConfig, ralsConn, cdrsConn, cfg.DefaultTimezone)
smRpc.SMs = append(smRpc.SMs, sm)
if err = sm.Connect(); err != nil {
utils.Logger.Err(fmt.Sprintf("<SM-FreeSWITCH> error: %s!", err))
utils.Logger.Err(fmt.Sprintf("<SMFreeSWITCH> error: %s!", err))
}
exitChan <- true
}
func startSmKamailio(internalRaterChan chan *engine.Responder, cdrDb engine.CdrStorage, exitChan chan bool) {
utils.Logger.Info("Starting CGRateS SM-Kamailio service.")
var raterConn, cdrsConn engine.Connector
var client *rpcclient.RpcClient
var err error
// Connect to rater
for _, raterCfg := range cfg.SmKamConfig.HaRater {
if raterCfg.Server == utils.INTERNAL {
resp := <-internalRaterChan
raterConn = resp // Will overwrite here for the sake of keeping internally the new configuration format for ha connections
internalRaterChan <- resp
} else {
client, err = rpcclient.NewRpcClient("tcp", raterCfg.Server, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil)
if err != nil { //Connected so no need to reiterate
utils.Logger.Crit(fmt.Sprintf("<SM-Kamailio> Could not connect to rater via RPC: %v", err))
exitChan <- true
return
}
raterConn = &engine.RPCClientConnector{Client: client}
func startSmKamailio(internalRaterChan, internalCDRSChan chan rpcclient.RpcClientConnection, cdrDb engine.CdrStorage, exitChan chan bool) {
utils.Logger.Info("Starting CGRateS SMKamailio service.")
var ralsConn, cdrsConn *rpcclient.RpcClientPool
if len(cfg.SmKamConfig.RALsConns) != 0 {
ralsConn, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cfg.SmKamConfig.RALsConns, internalRaterChan, cfg.InternalTtl)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<SMKamailio> Could not connect to RAL: %s", err.Error()))
exitChan <- true
return
}
}
// Connect to CDRS
if reflect.DeepEqual(cfg.SmKamConfig.HaCdrs, cfg.SmKamConfig.HaRater) {
cdrsConn = raterConn
} else if len(cfg.SmKamConfig.HaCdrs) != 0 {
for _, cdrsCfg := range cfg.SmKamConfig.HaCdrs {
if cdrsCfg.Server == utils.INTERNAL {
resp := <-internalRaterChan
cdrsConn = resp
internalRaterChan <- resp
} else {
client, err = rpcclient.NewRpcClient("tcp", cdrsCfg.Server, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<SM-Kamailio> Could not connect to CDRS via RPC: %v", err))
exitChan <- true
return
}
cdrsConn = &engine.RPCClientConnector{Client: client}
}
if len(cfg.SmKamConfig.CDRsConns) != 0 {
cdrsConn, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cfg.SmKamConfig.CDRsConns, internalCDRSChan, cfg.InternalTtl)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<SMKamailio> Could not connect to RAL: %s", err.Error()))
exitChan <- true
return
}
}
sm, _ := sessionmanager.NewKamailioSessionManager(cfg.SmKamConfig, raterConn, cdrsConn, cfg.DefaultTimezone)
sm, _ := sessionmanager.NewKamailioSessionManager(cfg.SmKamConfig, ralsConn, cdrsConn, cfg.DefaultTimezone)
smRpc.SMs = append(smRpc.SMs, sm)
if err = sm.Connect(); err != nil {
utils.Logger.Err(fmt.Sprintf("<SM-Kamailio> error: %s!", err))
utils.Logger.Err(fmt.Sprintf("<SMKamailio> error: %s!", err))
}
exitChan <- true
}
func startSmOpenSIPS(internalRaterChan chan *engine.Responder, cdrDb engine.CdrStorage, exitChan chan bool) {
utils.Logger.Info("Starting CGRateS SM-OpenSIPS service.")
var raterConn, cdrsConn engine.Connector
var client *rpcclient.RpcClient
var err error
// Connect to rater
for _, raterCfg := range cfg.SmOsipsConfig.HaRater {
if raterCfg.Server == utils.INTERNAL {
resp := <-internalRaterChan
raterConn = resp // Will overwrite here for the sake of keeping internally the new configuration format for ha connections
internalRaterChan <- resp
} else {
client, err = rpcclient.NewRpcClient("tcp", raterCfg.Server, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil)
if err != nil { //Connected so no need to reiterate
utils.Logger.Crit(fmt.Sprintf("<SM-OpenSIPS> Could not connect to rater via RPC: %v", err))
exitChan <- true
return
}
raterConn = &engine.RPCClientConnector{Client: client}
func startSmOpenSIPS(internalRaterChan, internalCDRSChan chan rpcclient.RpcClientConnection, cdrDb engine.CdrStorage, exitChan chan bool) {
utils.Logger.Info("Starting CGRateS SMOpenSIPS service.")
var ralsConn, cdrsConn *rpcclient.RpcClientPool
if len(cfg.SmOsipsConfig.RALsConns) != 0 {
ralsConn, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cfg.SmOsipsConfig.RALsConns, internalRaterChan, cfg.InternalTtl)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<SMOpenSIPS> Could not connect to RALs: %s", err.Error()))
exitChan <- true
return
}
}
// Connect to CDRS
if reflect.DeepEqual(cfg.SmOsipsConfig.HaCdrs, cfg.SmOsipsConfig.HaRater) {
cdrsConn = raterConn
} else if len(cfg.SmOsipsConfig.HaCdrs) != 0 {
for _, cdrsCfg := range cfg.SmOsipsConfig.HaCdrs {
if cdrsCfg.Server == utils.INTERNAL {
resp := <-internalRaterChan
cdrsConn = resp
internalRaterChan <- resp
} else {
client, err = rpcclient.NewRpcClient("tcp", cdrsCfg.Server, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<SM-OpenSIPS> Could not connect to CDRS via RPC: %v", err))
exitChan <- true
return
}
cdrsConn = &engine.RPCClientConnector{Client: client}
}
if len(cfg.SmOsipsConfig.CDRsConns) != 0 {
cdrsConn, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cfg.SmOsipsConfig.CDRsConns, internalRaterChan, cfg.InternalTtl)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<SMOpenSIPS> Could not connect to CDRs: %s", err.Error()))
exitChan <- true
return
}
}
sm, _ := sessionmanager.NewOSipsSessionManager(cfg.SmOsipsConfig, cfg.Reconnects, raterConn, cdrsConn, cfg.DefaultTimezone)
sm, _ := sessionmanager.NewOSipsSessionManager(cfg.SmOsipsConfig, cfg.Reconnects, ralsConn, cdrsConn, cfg.DefaultTimezone)
smRpc.SMs = append(smRpc.SMs, sm)
if err := sm.Connect(); err != nil {
utils.Logger.Err(fmt.Sprintf("<SM-OpenSIPS> error: %s!", err))
@@ -383,102 +288,60 @@ func startSmOpenSIPS(internalRaterChan chan *engine.Responder, cdrDb engine.CdrS
exitChan <- true
}
func startCDRS(internalCdrSChan chan *engine.CdrServer, logDb engine.LogStorage, cdrDb engine.CdrStorage,
internalRaterChan chan *engine.Responder, internalPubSubSChan chan rpcclient.RpcClientConnection,
internalUserSChan chan engine.UserService, internalAliaseSChan chan engine.AliasService,
internalCdrStatSChan chan engine.StatsInterface, server *utils.Server, exitChan chan bool) {
func startCDRS(internalCdrSChan chan rpcclient.RpcClientConnection, logDb engine.LogStorage, cdrDb engine.CdrStorage,
internalRaterChan chan rpcclient.RpcClientConnection, internalPubSubSChan chan rpcclient.RpcClientConnection,
internalUserSChan chan rpcclient.RpcClientConnection, internalAliaseSChan chan rpcclient.RpcClientConnection,
internalCdrStatSChan chan rpcclient.RpcClientConnection, server *utils.Server, exitChan chan bool) {
utils.Logger.Info("Starting CGRateS CDRS service.")
var err error
var client *rpcclient.RpcClient
// Rater connection init
var raterConn engine.Connector
if cfg.CDRSRater == utils.INTERNAL {
responder := <-internalRaterChan // Wait for rater to come up before start querying
raterConn = responder
internalRaterChan <- responder // Put back the connection since there might be other entities waiting for it
} else if len(cfg.CDRSRater) != 0 {
client, err = rpcclient.NewRpcClient("tcp", cfg.CDRSRater, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil)
var ralConn, pubSubConn, usersConn, aliasesConn, statsConn *rpcclient.RpcClientPool
if len(cfg.CDRSRaterConns) != 0 { // Conn pool towards RAL
ralConn, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cfg.CDRSRaterConns, internalRaterChan, cfg.InternalTtl)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<CDRS> Could not connect to rater: %s", err.Error()))
utils.Logger.Crit(fmt.Sprintf("<CDRS> Could not connect to RAL: %s", err.Error()))
exitChan <- true
return
}
raterConn = &engine.RPCClientConnector{Client: client}
}
// Pubsub connection init
var pubSubConn rpcclient.RpcClientConnection
if cfg.CDRSPubSub == utils.INTERNAL {
pubSubs := <-internalPubSubSChan
pubSubConn = pubSubs
internalPubSubSChan <- pubSubs
} else if len(cfg.CDRSPubSub) != 0 {
client, err = rpcclient.NewRpcClient("tcp", cfg.CDRSPubSub, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil)
if len(cfg.CDRSPubSubSConns) != 0 { // Pubsub connection init
pubSubConn, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cfg.CDRSPubSubSConns, internalPubSubSChan, cfg.InternalTtl)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<CDRS> Could not connect to pubsub server: %s", err.Error()))
utils.Logger.Crit(fmt.Sprintf("<CDRS> Could not connect to PubSubSystem: %s", err.Error()))
exitChan <- true
return
}
pubSubConn = client
}
// Users connection init
var usersConn engine.UserService
if cfg.CDRSUsers == utils.INTERNAL {
userS := <-internalUserSChan
usersConn = userS
internalUserSChan <- userS
} else if len(cfg.CDRSUsers) != 0 {
if cfg.CDRSRater == cfg.CDRSUsers {
usersConn = &engine.ProxyUserService{Client: client}
} else {
client, err = rpcclient.NewRpcClient("tcp", cfg.CDRSUsers, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<CDRS> Could not connect to users server: %s", err.Error()))
exitChan <- true
return
}
usersConn = &engine.ProxyUserService{Client: client}
if len(cfg.CDRSUserSConns) != 0 { // Users connection init
usersConn, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cfg.CDRSUserSConns, internalUserSChan, cfg.InternalTtl)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<CDRS> Could not connect to UserS: %s", err.Error()))
exitChan <- true
return
}
}
// Aliases connection init
var aliasesConn engine.AliasService
if cfg.CDRSAliases == utils.INTERNAL {
aliaseS := <-internalAliaseSChan
aliasesConn = aliaseS
internalAliaseSChan <- aliaseS
} else if len(cfg.CDRSAliases) != 0 {
if cfg.CDRSRater == cfg.CDRSAliases {
aliasesConn = &engine.ProxyAliasService{Client: client}
} else {
client, err = rpcclient.NewRpcClient("tcp", cfg.CDRSAliases, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<CDRS> Could not connect to aliases server: %s", err.Error()))
exitChan <- true
return
}
aliasesConn = &engine.ProxyAliasService{Client: client}
}
}
// Stats connection init
var statsConn engine.StatsInterface
if cfg.CDRSStats == utils.INTERNAL {
statS := <-internalCdrStatSChan
statsConn = statS
internalCdrStatSChan <- statS
} else if len(cfg.CDRSStats) != 0 {
if cfg.CDRSRater == cfg.CDRSStats {
statsConn = &engine.ProxyStats{Client: client}
} else {
client, err = rpcclient.NewRpcClient("tcp", cfg.CDRSStats, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<CDRS> Could not connect to stats server: %s", err.Error()))
exitChan <- true
return
}
statsConn = &engine.ProxyStats{Client: client}
if len(cfg.CDRSAliaseSConns) != 0 { // Aliases connection init
aliasesConn, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cfg.CDRSAliaseSConns, internalAliaseSChan, cfg.InternalTtl)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<CDRS> Could not connect to AliaseS: %s", err.Error()))
exitChan <- true
return
}
}
cdrServer, _ := engine.NewCdrServer(cfg, cdrDb, raterConn, pubSubConn, usersConn, aliasesConn, statsConn)
if len(cfg.CDRSStatSConns) != 0 { // Stats connection init
statsConn, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cfg.CDRSStatSConns, internalCdrStatSChan, cfg.InternalTtl)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<CDRS> Could not connect to StatS: %s", err.Error()))
exitChan <- true
return
}
}
cdrServer, _ := engine.NewCdrServer(cfg, cdrDb, ralConn, pubSubConn, usersConn, aliasesConn, statsConn)
cdrServer.SetTimeToLive(cfg.ResponseCacheTTL, nil)
utils.Logger.Info("Registering CDRS HTTP Handlers.")
cdrServer.RegisterHandlersToServer(server)
utils.Logger.Info("Registering CDRS RPC service.")
@@ -486,10 +349,8 @@ func startCDRS(internalCdrSChan chan *engine.CdrServer, logDb engine.LogStorage,
server.RpcRegister(&cdrSrv)
server.RpcRegister(&v2.CdrsV2{CdrsV1: cdrSrv})
// Make the cdr server available for internal communication
responder := <-internalRaterChan // Retrieve again the responder
responder.CdrSrv = cdrServer // Attach connection to cdrServer in responder, so it can be used later
internalRaterChan <- responder // Put back the connection for the rest of the system
internalCdrSChan <- cdrServer // Signal that cdrS is operational
server.RpcRegister(cdrServer) // register CdrServer for internal usage (TODO: refactor this)
internalCdrSChan <- cdrServer // Signal that cdrS is operational
}
func startScheduler(internalSchedulerChan chan *scheduler.Scheduler, cacheDoneChan chan struct{}, ratingDb engine.RatingStorage, exitChan chan bool) {
@@ -506,20 +367,20 @@ func startScheduler(internalSchedulerChan chan *scheduler.Scheduler, cacheDoneCh
exitChan <- true // Should not get out of loop though
}
func startCdrStats(internalCdrStatSChan chan engine.StatsInterface, ratingDb engine.RatingStorage, accountDb engine.AccountingStorage, server *utils.Server) {
func startCdrStats(internalCdrStatSChan chan rpcclient.RpcClientConnection, ratingDb engine.RatingStorage, accountDb engine.AccountingStorage, server *utils.Server) {
cdrStats := engine.NewStats(ratingDb, accountDb, cfg.CDRStatsSaveInterval)
server.RpcRegister(cdrStats)
server.RpcRegister(&v1.CDRStatsV1{CdrStats: cdrStats}) // Public APIs
internalCdrStatSChan <- cdrStats
}
func startHistoryServer(internalHistorySChan chan history.Scribe, server *utils.Server, exitChan chan bool) {
func startHistoryServer(internalHistorySChan chan rpcclient.RpcClientConnection, server *utils.Server, exitChan chan bool) {
scribeServer, err := history.NewFileScribe(cfg.HistoryDir, cfg.HistorySaveInterval)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<HistoryServer> Could not start, error: %s", err.Error()))
exitChan <- true
}
server.RpcRegisterName("ScribeV1", scribeServer)
server.RpcRegisterName("HistoryV1", scribeServer)
internalHistorySChan <- scribeServer
}
@@ -530,7 +391,7 @@ func startPubSubServer(internalPubSubSChan chan rpcclient.RpcClientConnection, a
}
// ToDo: Make sure we are caching before starting this one
func startAliasesServer(internalAliaseSChan chan engine.AliasService, accountDb engine.AccountingStorage, server *utils.Server, exitChan chan bool) {
func startAliasesServer(internalAliaseSChan chan rpcclient.RpcClientConnection, accountDb engine.AccountingStorage, server *utils.Server, exitChan chan bool) {
aliasesServer := engine.NewAliasHandler(accountDb)
server.RpcRegisterName("AliasesV1", aliasesServer)
if err := accountDb.CacheAccountingPrefixes(utils.ALIASES_PREFIX); err != nil {
@@ -541,7 +402,7 @@ func startAliasesServer(internalAliaseSChan chan engine.AliasService, accountDb
internalAliaseSChan <- aliasesServer
}
func startUsersServer(internalUserSChan chan engine.UserService, accountDb engine.AccountingStorage, server *utils.Server, exitChan chan bool) {
func startUsersServer(internalUserSChan chan rpcclient.RpcClientConnection, accountDb engine.AccountingStorage, server *utils.Server, exitChan chan bool) {
userServer, err := engine.NewUserMap(accountDb, cfg.UserServerIndexes)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<UsersService> Could not start, error: %s", err.Error()))
@@ -552,13 +413,9 @@ func startUsersServer(internalUserSChan chan engine.UserService, accountDb engin
internalUserSChan <- userServer
}
func startRpc(server *utils.Server, internalRaterChan chan *engine.Responder,
internalCdrSChan chan *engine.CdrServer,
internalCdrStatSChan chan engine.StatsInterface,
internalHistorySChan chan history.Scribe,
internalPubSubSChan chan rpcclient.RpcClientConnection,
internalUserSChan chan engine.UserService,
internalAliaseSChan chan engine.AliasService) {
func startRpc(server *utils.Server, internalRaterChan,
internalCdrSChan, internalCdrStatSChan, internalHistorySChan, internalPubSubSChan, internalUserSChan,
internalAliaseSChan chan rpcclient.RpcClientConnection) {
select { // Any of the rpc methods will unlock listening to rpc requests
case resp := <-internalRaterChan:
internalRaterChan <- resp
@@ -619,7 +476,7 @@ func main() {
}
config.SetCgrConfig(cfg) // Share the config object
if *raterEnabled {
cfg.RaterEnabled = *raterEnabled
cfg.RALsEnabled = *raterEnabled
}
if *schedEnabled {
cfg.SchedulerEnabled = *schedEnabled
@@ -632,7 +489,7 @@ func main() {
var logDb engine.LogStorage
var loadDb engine.LoadStorage
var cdrDb engine.CdrStorage
if cfg.RaterEnabled || cfg.SchedulerEnabled || cfg.CDRStatsEnabled { // Only connect to dataDb if necessary
if cfg.RALsEnabled || cfg.SchedulerEnabled || cfg.CDRStatsEnabled { // Only connect to dataDb if necessary
ratingDb, err = engine.ConfigureRatingStorage(cfg.TpDbType, cfg.TpDbHost, cfg.TpDbPort,
cfg.TpDbName, cfg.TpDbUser, cfg.TpDbPass, cfg.DBDataEncoding)
if err != nil { // Cannot configure getter database, show stopper
@@ -642,7 +499,7 @@ func main() {
defer ratingDb.Close()
engine.SetRatingStorage(ratingDb)
}
if cfg.RaterEnabled || cfg.CDRStatsEnabled || cfg.PubSubServerEnabled || cfg.AliasesServerEnabled || cfg.UserServerEnabled {
if cfg.RALsEnabled || cfg.CDRStatsEnabled || cfg.PubSubServerEnabled || cfg.AliasesServerEnabled || cfg.UserServerEnabled {
accountDb, err = engine.ConfigureAccountingStorage(cfg.DataDbType, cfg.DataDbHost, cfg.DataDbPort,
cfg.DataDbName, cfg.DataDbUser, cfg.DataDbPass, cfg.DBDataEncoding)
if err != nil { // Cannot configure getter database, show stopper
@@ -651,8 +508,12 @@ func main() {
}
defer accountDb.Close()
engine.SetAccountingStorage(accountDb)
if err := engine.CheckVersion(); err != nil {
fmt.Println(err.Error())
return
}
}
if cfg.RaterEnabled || cfg.CDRSEnabled || cfg.SchedulerEnabled { // Only connect to storDb if necessary
if cfg.RALsEnabled || cfg.CDRSEnabled || cfg.SchedulerEnabled { // Only connect to storDb if necessary
logDb, err = engine.ConfigureLogStorage(cfg.StorDBType, cfg.StorDBHost, cfg.StorDBPort,
cfg.StorDBName, cfg.StorDBUser, cfg.StorDBPass, cfg.DBDataEncoding, cfg.StorDBMaxOpenConns, cfg.StorDBMaxIdleConns, cfg.StorDBCDRSIndexes)
if err != nil { // Cannot configure logger database, show stopper
@@ -668,6 +529,8 @@ func main() {
}
engine.SetRoundingDecimals(cfg.RoundingDecimals)
engine.SetRpSubjectPrefixMatching(cfg.RpSubjectPrefixMatching)
engine.SetLcrSubjectPrefixMatching(cfg.LcrSubjectPrefixMatching)
stopHandled := false
// Rpc/http server
@@ -678,15 +541,15 @@ func main() {
// Define internal connections via channels
internalBalancerChan := make(chan *balancer2go.Balancer, 1)
internalRaterChan := make(chan *engine.Responder, 1)
internalRaterChan := make(chan rpcclient.RpcClientConnection, 1)
cacheDoneChan := make(chan struct{}, 1)
internalSchedulerChan := make(chan *scheduler.Scheduler, 1)
internalCdrSChan := make(chan *engine.CdrServer, 1)
internalCdrStatSChan := make(chan engine.StatsInterface, 1)
internalHistorySChan := make(chan history.Scribe, 1)
internalCdrSChan := make(chan rpcclient.RpcClientConnection, 1)
internalCdrStatSChan := make(chan rpcclient.RpcClientConnection, 1)
internalHistorySChan := make(chan rpcclient.RpcClientConnection, 1)
internalPubSubSChan := make(chan rpcclient.RpcClientConnection, 1)
internalUserSChan := make(chan engine.UserService, 1)
internalAliaseSChan := make(chan engine.AliasService, 1)
internalUserSChan := make(chan rpcclient.RpcClientConnection, 1)
internalAliaseSChan := make(chan rpcclient.RpcClientConnection, 1)
internalSMGChan := make(chan rpcclient.RpcClientConnection, 1)
// Start balancer service
if cfg.BalancerEnabled {
@@ -694,7 +557,7 @@ func main() {
}
// Start rater service
if cfg.RaterEnabled {
if cfg.RALsEnabled {
go startRater(internalRaterChan, cacheDoneChan, internalBalancerChan, internalSchedulerChan, internalCdrStatSChan, internalHistorySChan, internalPubSubSChan, internalUserSChan, internalAliaseSChan,
server, ratingDb, accountDb, loadDb, cdrDb, logDb, &stopHandled, exitChan)
}
@@ -719,23 +582,23 @@ func main() {
// Start SM-Generic
if cfg.SmGenericConfig.Enabled {
go startSmGeneric(internalSMGChan, internalRaterChan, server, exitChan)
go startSmGeneric(internalSMGChan, internalRaterChan, internalCdrSChan, server, exitChan)
}
// Start SM-FreeSWITCH
if cfg.SmFsConfig.Enabled {
go startSmFreeSWITCH(internalRaterChan, cdrDb, exitChan)
go startSmFreeSWITCH(internalRaterChan, internalCdrSChan, cdrDb, exitChan)
// close all sessions on shutdown
go shutdownSessionmanagerSingnalHandler(exitChan)
}
// Start SM-Kamailio
if cfg.SmKamConfig.Enabled {
go startSmKamailio(internalRaterChan, cdrDb, exitChan)
go startSmKamailio(internalRaterChan, internalCdrSChan, cdrDb, exitChan)
}
// Start SM-OpenSIPS
if cfg.SmOsipsConfig.Enabled {
go startSmOpenSIPS(internalRaterChan, cdrDb, exitChan)
go startSmOpenSIPS(internalRaterChan, internalCdrSChan, cdrDb, exitChan)
}
// Register session manager service // FixMe: make sure this is thread safe

View File

@@ -40,9 +40,10 @@ func startBalancer(internalBalancerChan chan *balancer2go.Balancer, stopHandled
}
// Starts rater and reports on chan
func startRater(internalRaterChan chan *engine.Responder, cacheDoneChan chan struct{}, internalBalancerChan chan *balancer2go.Balancer, internalSchedulerChan chan *scheduler.Scheduler,
internalCdrStatSChan chan engine.StatsInterface, internalHistorySChan chan history.Scribe,
internalPubSubSChan chan rpcclient.RpcClientConnection, internalUserSChan chan engine.UserService, internalAliaseSChan chan engine.AliasService,
func startRater(internalRaterChan chan rpcclient.RpcClientConnection, cacheDoneChan chan struct{}, internalBalancerChan chan *balancer2go.Balancer, internalSchedulerChan chan *scheduler.Scheduler,
internalCdrStatSChan chan rpcclient.RpcClientConnection, internalHistorySChan chan rpcclient.RpcClientConnection,
internalPubSubSChan chan rpcclient.RpcClientConnection, internalUserSChan chan rpcclient.RpcClientConnection, internalAliaseSChan chan rpcclient.RpcClientConnection,
server *utils.Server,
ratingDb engine.RatingStorage, accountDb engine.AccountingStorage, loadDb engine.LoadStorage, cdrDb engine.CdrStorage, logDb engine.LogStorage,
stopHandled *bool, exitChan chan bool) {
@@ -84,15 +85,13 @@ func startRater(internalRaterChan chan *engine.Responder, cacheDoneChan chan str
}()
}
// Connection to balancer
var bal *balancer2go.Balancer
if cfg.RaterBalancer != "" {
if cfg.RALsBalancer != "" { // Connection to balancer
balTaskChan := make(chan struct{})
waitTasks = append(waitTasks, balTaskChan)
go func() {
defer close(balTaskChan)
if cfg.RaterBalancer == utils.INTERNAL {
if cfg.RALsBalancer == utils.MetaInternal {
select {
case bal = <-internalBalancerChan:
internalBalancerChan <- bal // Put it back if someone else is interested about
@@ -108,144 +107,117 @@ func startRater(internalRaterChan chan *engine.Responder, cacheDoneChan chan str
}
}()
}
// Connection to CDRStats
var cdrStats engine.StatsInterface
if cfg.RaterCdrStats != "" {
var cdrStats *rpcclient.RpcClientPool
if len(cfg.RALsCDRStatSConns) != 0 { // Connections to CDRStats
cdrstatTaskChan := make(chan struct{})
waitTasks = append(waitTasks, cdrstatTaskChan)
go func() {
defer close(cdrstatTaskChan)
if cfg.RaterCdrStats == utils.INTERNAL {
select {
case cdrStats = <-internalCdrStatSChan:
internalCdrStatSChan <- cdrStats
case <-time.After(cfg.InternalTtl):
utils.Logger.Crit("<Rater>: Internal cdrstats connection timeout.")
exitChan <- true
return
}
} else if cdrStats, err = engine.NewProxyStats(cfg.RaterCdrStats, cfg.ConnectAttempts, -1); err != nil {
utils.Logger.Crit(fmt.Sprintf("<Rater> Could not connect to cdrstats, error: %s", err.Error()))
cdrStats, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cfg.RALsCDRStatSConns, internalCdrStatSChan, cfg.InternalTtl)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("<RALs> Could not connect to CDRStatS, error: %s", err.Error()))
exitChan <- true
return
}
}()
}
// Connection to HistoryS
if cfg.RaterHistoryServer != "" {
if len(cfg.RALsHistorySConns) != 0 { // Connection to HistoryS,
histTaskChan := make(chan struct{})
waitTasks = append(waitTasks, histTaskChan)
go func() {
defer close(histTaskChan)
var scribeServer history.Scribe
if cfg.RaterHistoryServer == utils.INTERNAL {
select {
case scribeServer = <-internalHistorySChan:
internalHistorySChan <- scribeServer
case <-time.After(cfg.InternalTtl):
utils.Logger.Crit("<Rater>: Internal historys connection timeout.")
exitChan <- true
return
}
} else if scribeServer, err = history.NewProxyScribe(cfg.RaterHistoryServer, cfg.ConnectAttempts, -1); err != nil {
utils.Logger.Crit(fmt.Sprintf("<Rater> Could not connect historys, error: %s", err.Error()))
if historySConns, err := engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cfg.RALsHistorySConns, internalHistorySChan, cfg.InternalTtl); err != nil {
utils.Logger.Crit(fmt.Sprintf("<RALs> Could not connect HistoryS, error: %s", err.Error()))
exitChan <- true
return
} else {
engine.SetHistoryScribe(historySConns)
}
engine.SetHistoryScribe(scribeServer) // ToDo: replace package sharing with connection based one
}()
}
// Connection to pubsubs
if cfg.RaterPubSubServer != "" {
if len(cfg.RALsPubSubSConns) != 0 { // Connection to pubsubs
pubsubTaskChan := make(chan struct{})
waitTasks = append(waitTasks, pubsubTaskChan)
go func() {
defer close(pubsubTaskChan)
var pubSubServer rpcclient.RpcClientConnection
if cfg.RaterPubSubServer == utils.INTERNAL {
select {
case pubSubServer = <-internalPubSubSChan:
internalPubSubSChan <- pubSubServer
case <-time.After(cfg.InternalTtl):
utils.Logger.Crit("<Rater>: Internal pubsub connection timeout.")
exitChan <- true
return
}
} else if pubSubServer, err = rpcclient.NewRpcClient("tcp", cfg.RaterPubSubServer, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil); err != nil {
utils.Logger.Crit(fmt.Sprintf("<Rater> Could not connect to pubsubs: %s", err.Error()))
if pubSubSConns, err := engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cfg.RALsPubSubSConns, internalPubSubSChan, cfg.InternalTtl); err != nil {
utils.Logger.Crit(fmt.Sprintf("<RALs> Could not connect to PubSubS: %s", err.Error()))
exitChan <- true
return
} else {
engine.SetPubSub(pubSubSConns)
}
engine.SetPubSub(pubSubServer) // ToDo: replace package sharing with connection based one
}()
}
// Connection to AliasService
if cfg.RaterAliasesServer != "" {
if len(cfg.RALsAliasSConns) != 0 { // Connection to AliasService
aliasesTaskChan := make(chan struct{})
waitTasks = append(waitTasks, aliasesTaskChan)
go func() {
defer close(aliasesTaskChan)
var aliasesServer engine.AliasService
if cfg.RaterAliasesServer == utils.INTERNAL {
select {
case aliasesServer = <-internalAliaseSChan:
internalAliaseSChan <- aliasesServer
case <-time.After(cfg.InternalTtl):
utils.Logger.Crit("<Rater>: Internal aliases connection timeout.")
exitChan <- true
return
}
} else if aliasesServer, err = engine.NewProxyAliasService(cfg.RaterAliasesServer, cfg.ConnectAttempts, -1); err != nil {
utils.Logger.Crit(fmt.Sprintf("<Rater> Could not connect to aliases, error: %s", err.Error()))
if aliaseSCons, err := engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cfg.RALsAliasSConns, internalAliaseSChan, cfg.InternalTtl); err != nil {
utils.Logger.Crit(fmt.Sprintf("<RALs> Could not connect to AliaseS, error: %s", err.Error()))
exitChan <- true
return
} else {
engine.SetAliasService(aliaseSCons)
}
engine.SetAliasService(aliasesServer) // ToDo: replace package sharing with connection based one
}()
}
// Connection to UserService
var userServer engine.UserService
if cfg.RaterUserServer != "" {
var usersConns rpcclient.RpcClientConnection
if len(cfg.RALsUserSConns) != 0 { // Connection to UserService
usersTaskChan := make(chan struct{})
waitTasks = append(waitTasks, usersTaskChan)
go func() {
defer close(usersTaskChan)
if cfg.RaterUserServer == utils.INTERNAL {
select {
case userServer = <-internalUserSChan:
internalUserSChan <- userServer
case <-time.After(cfg.InternalTtl):
utils.Logger.Crit("<Rater>: Internal users connection timeout.")
exitChan <- true
return
}
} else if userServer, err = engine.NewProxyUserService(cfg.RaterUserServer, cfg.ConnectAttempts, -1); err != nil {
utils.Logger.Crit(fmt.Sprintf("<Rater> Could not connect users, error: %s", err.Error()))
if usersConns, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB,
cfg.RALsUserSConns, internalUserSChan, cfg.InternalTtl); err != nil {
utils.Logger.Crit(fmt.Sprintf("<RALs> Could not connect UserS, error: %s", err.Error()))
exitChan <- true
return
}
engine.SetUserService(userServer)
engine.SetUserService(usersConns)
}()
}
// Wait for all connections to complete before going further
for _, chn := range waitTasks {
<-chn
}
responder := &engine.Responder{Bal: bal, ExitChan: exitChan, Stats: cdrStats}
responder := &engine.Responder{Bal: bal, ExitChan: exitChan}
responder.SetTimeToLive(cfg.ResponseCacheTTL, nil)
apierRpcV1 := &v1.ApierV1{StorDb: loadDb, RatingDb: ratingDb, AccountDb: accountDb, CdrDb: cdrDb, LogDb: logDb, Sched: sched,
Config: cfg, Responder: responder, CdrStatsSrv: cdrStats, Users: userServer}
Config: cfg, Responder: responder}
if cdrStats != nil { // ToDo: Fix here properly the init of stats
responder.Stats = cdrStats
apierRpcV1.CdrStatsSrv = cdrStats
}
if usersConns != nil {
apierRpcV1.Users = usersConns
}
apierRpcV2 := &v2.ApierV2{
ApierV1: *apierRpcV1}
// internalSchedulerChan shared here
server.RpcRegister(responder)
server.RpcRegister(apierRpcV1)
server.RpcRegister(apierRpcV2)
utils.RegisterRpcParams("", &engine.Stats{})
utils.RegisterRpcParams("", &v1.CDRStatsV1{})
utils.RegisterRpcParams("ScribeV1", &history.FileScribe{})
utils.RegisterRpcParams("PubSubV1", &engine.PubSub{})
utils.RegisterRpcParams("AliasesV1", &engine.AliasHandler{})
utils.RegisterRpcParams("UsersV1", &engine.UserMap{})
utils.RegisterRpcParams("", &v1.CdrsV1{})
utils.RegisterRpcParams("", &v2.CdrsV2{})
utils.RegisterRpcParams("", &v1.SessionManagerV1{})
utils.RegisterRpcParams("", &v1.SMGenericV1{})
utils.RegisterRpcParams("", responder)
utils.RegisterRpcParams("", apierRpcV1)
utils.RegisterRpcParams("", apierRpcV2)
utils.GetRpcParams("")
internalRaterChan <- responder // Rater done
}

View File

@@ -29,6 +29,7 @@ import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/scheduler"
"github.com/cgrates/cgrates/utils"
"github.com/cgrates/rpcclient"
)
/*
@@ -43,7 +44,7 @@ func stopBalancerSignalHandler(bal *balancer2go.Balancer, exitChan chan bool) {
exitChan <- true
}
func generalSignalHandler(internalCdrStatSChan chan engine.StatsInterface, exitChan chan bool) {
func generalSignalHandler(internalCdrStatSChan chan rpcclient.RpcClientConnection, exitChan chan bool) {
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
@@ -52,7 +53,7 @@ func generalSignalHandler(internalCdrStatSChan chan engine.StatsInterface, exitC
var dummyInt int
select {
case cdrStats := <-internalCdrStatSChan:
cdrStats.Stop(dummyInt, &dummyInt)
cdrStats.Call("CDRStatsV1.Stop", dummyInt, &dummyInt)
default:
}
@@ -62,7 +63,7 @@ func generalSignalHandler(internalCdrStatSChan chan engine.StatsInterface, exitC
/*
Listens for the SIGTERM, SIGINT, SIGQUIT system signals and gracefuly unregister from balancer and closes the storage before exiting.
*/
func stopRaterSignalHandler(internalCdrStatSChan chan engine.StatsInterface, exitChan chan bool) {
func stopRaterSignalHandler(internalCdrStatSChan chan rpcclient.RpcClientConnection, exitChan chan bool) {
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
sig := <-c
@@ -72,7 +73,7 @@ func stopRaterSignalHandler(internalCdrStatSChan chan engine.StatsInterface, exi
var dummyInt int
select {
case cdrStats := <-internalCdrStatSChan:
cdrStats.Stop(dummyInt, &dummyInt)
cdrStats.Call("CDRStatsV1.Stop", dummyInt, &dummyInt)
default:
}
exitChan <- true
@@ -82,14 +83,14 @@ func stopRaterSignalHandler(internalCdrStatSChan chan engine.StatsInterface, exi
Connects to the balancer and calls unregister RPC method.
*/
func unregisterFromBalancer(exitChan chan bool) {
client, err := rpc.Dial("tcp", cfg.RaterBalancer)
client, err := rpc.Dial("tcp", cfg.RALsBalancer)
if err != nil {
utils.Logger.Crit("Cannot contact the balancer!")
exitChan <- true
return
}
var reply int
utils.Logger.Info(fmt.Sprintf("Unregistering from balancer %s", cfg.RaterBalancer))
utils.Logger.Info(fmt.Sprintf("Unregistering from balancer %s", cfg.RALsBalancer))
client.Call("Responder.UnRegisterRater", cfg.RPCGOBListen, &reply)
if err := client.Close(); err != nil {
utils.Logger.Crit("Could not close balancer unregistration!")
@@ -101,14 +102,14 @@ func unregisterFromBalancer(exitChan chan bool) {
Connects to the balancer and rehisters the engine to the server.
*/
func registerToBalancer(exitChan chan bool) {
client, err := rpc.Dial("tcp", cfg.RaterBalancer)
client, err := rpc.Dial("tcp", cfg.RALsBalancer)
if err != nil {
utils.Logger.Crit(fmt.Sprintf("Cannot contact the balancer: %v", err))
exitChan <- true
return
}
var reply int
utils.Logger.Info(fmt.Sprintf("Registering to balancer %s", cfg.RaterBalancer))
utils.Logger.Info(fmt.Sprintf("Registering to balancer %s", cfg.RALsBalancer))
client.Call("Responder.RegisterRater", cfg.RPCGOBListen, &reply)
if err := client.Close(); err != nil {
utils.Logger.Crit("Could not close balancer registration!")

View File

@@ -29,8 +29,8 @@ import (
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/history"
"github.com/cgrates/cgrates/utils"
"github.com/cgrates/rpcclient"
)
var (
@@ -92,77 +92,107 @@ func main() {
var rater, cdrstats, users *rpc.Client
var loader engine.LoadReader
if *migrateRC8 != "" {
var db_nb int
db_nb, err = strconv.Atoi(*datadb_name)
if err != nil {
log.Print("Redis db name must be an integer!")
return
}
host := *datadb_host
if *datadb_port != "" {
host += ":" + *datadb_port
}
migratorRC8acc, err := NewMigratorRC8(host, db_nb, *datadb_pass, *dbdata_encoding)
if err != nil {
log.Print(err.Error())
return
}
if strings.Contains(*migrateRC8, "acc") || strings.Contains(*migrateRC8, "*all") {
if err := migratorRC8acc.migrateAccounts(); err != nil {
if *datadb_type == "redis" && *tpdb_type == "redis" {
var db_nb int
db_nb, err = strconv.Atoi(*datadb_name)
if err != nil {
log.Print("Redis db name must be an integer!")
return
}
host := *datadb_host
if *datadb_port != "" {
host += ":" + *datadb_port
}
migratorRC8acc, err := NewMigratorRC8(host, db_nb, *datadb_pass, *dbdata_encoding)
if err != nil {
log.Print(err.Error())
return
}
if strings.Contains(*migrateRC8, "acc") || strings.Contains(*migrateRC8, "*all") {
if err := migratorRC8acc.migrateAccounts(); err != nil {
log.Print(err.Error())
}
}
db_nb, err = strconv.Atoi(*tpdb_name)
if err != nil {
log.Print("Redis db name must be an integer!")
return
}
host = *tpdb_host
if *tpdb_port != "" {
host += ":" + *tpdb_port
}
migratorRC8rat, err := NewMigratorRC8(host, db_nb, *tpdb_pass, *dbdata_encoding)
if err != nil {
log.Print(err.Error())
return
}
if strings.Contains(*migrateRC8, "atr") || strings.Contains(*migrateRC8, "*all") {
if err := migratorRC8rat.migrateActionTriggers(); err != nil {
log.Print(err.Error())
}
}
if strings.Contains(*migrateRC8, "act") || strings.Contains(*migrateRC8, "*all") {
if err := migratorRC8rat.migrateActions(); err != nil {
log.Print(err.Error())
}
}
if strings.Contains(*migrateRC8, "dcs") || strings.Contains(*migrateRC8, "*all") {
if err := migratorRC8rat.migrateDerivedChargers(); err != nil {
log.Print(err.Error())
}
}
if strings.Contains(*migrateRC8, "apl") || strings.Contains(*migrateRC8, "*all") {
if err := migratorRC8rat.migrateActionPlans(); err != nil {
log.Print(err.Error())
}
}
if strings.Contains(*migrateRC8, "shg") || strings.Contains(*migrateRC8, "*all") {
if err := migratorRC8rat.migrateSharedGroups(); err != nil {
log.Print(err.Error())
}
}
if strings.Contains(*migrateRC8, "int") {
if err := migratorRC8acc.migrateAccountsInt(); err != nil {
log.Print(err.Error())
}
if err := migratorRC8rat.migrateActionTriggersInt(); err != nil {
log.Print(err.Error())
}
if err := migratorRC8rat.migrateActionsInt(); err != nil {
log.Print(err.Error())
}
}
if strings.Contains(*migrateRC8, "vf") {
if err := migratorRC8rat.migrateActionsInt2(); err != nil {
log.Print(err.Error())
}
if err := migratorRC8acc.writeVersion(); err != nil {
log.Print(err.Error())
}
}
} else if *datadb_type == "mongo" && *tpdb_type == "mongo" {
mongoMigratorAcc, err := NewMongoMigrator(*datadb_host, *datadb_port, *datadb_name, *datadb_user, *datadb_pass)
if err != nil {
log.Print(err.Error())
return
}
mongoMigratorRat, err := NewMongoMigrator(*tpdb_host, *tpdb_port, *tpdb_name, *tpdb_user, *tpdb_pass)
if err != nil {
log.Print(err.Error())
return
}
if strings.Contains(*migrateRC8, "vf") {
if err := mongoMigratorRat.migrateActions(); err != nil {
log.Print(err.Error())
}
if err := mongoMigratorAcc.writeVersion(); err != nil {
log.Print(err.Error())
}
}
}
db_nb, err = strconv.Atoi(*tpdb_name)
if err != nil {
log.Print("Redis db name must be an integer!")
return
}
host = *tpdb_host
if *tpdb_port != "" {
host += ":" + *tpdb_port
}
migratorRC8rat, err := NewMigratorRC8(host, db_nb, *tpdb_pass, *dbdata_encoding)
if err != nil {
log.Print(err.Error())
return
}
if strings.Contains(*migrateRC8, "atr") || strings.Contains(*migrateRC8, "*all") {
if err := migratorRC8rat.migrateActionTriggers(); err != nil {
log.Print(err.Error())
}
}
if strings.Contains(*migrateRC8, "act") || strings.Contains(*migrateRC8, "*all") {
if err := migratorRC8rat.migrateActions(); err != nil {
log.Print(err.Error())
}
}
if strings.Contains(*migrateRC8, "dcs") || strings.Contains(*migrateRC8, "*all") {
if err := migratorRC8rat.migrateDerivedChargers(); err != nil {
log.Print(err.Error())
}
}
if strings.Contains(*migrateRC8, "apl") || strings.Contains(*migrateRC8, "*all") {
if err := migratorRC8rat.migrateActionPlans(); err != nil {
log.Print(err.Error())
}
}
if strings.Contains(*migrateRC8, "shg") || strings.Contains(*migrateRC8, "*all") {
if err := migratorRC8rat.migrateSharedGroups(); err != nil {
log.Print(err.Error())
}
}
if strings.Contains(*migrateRC8, "int") {
if err := migratorRC8acc.migrateAccountsInt(); err != nil {
log.Print(err.Error())
}
if err := migratorRC8rat.migrateActionTriggersInt(); err != nil {
log.Print(err.Error())
}
if err := migratorRC8rat.migrateActionsInt(); err != nil {
log.Print(err.Error())
}
}
log.Print("Done!")
return
}
@@ -257,7 +287,7 @@ func main() {
return
}
if *historyServer != "" { // Init scribeAgent so we can store the differences
if scribeAgent, err := history.NewProxyScribe(*historyServer, 3, 3); err != nil {
if scribeAgent, err := rpcclient.NewRpcClient("tcp", *historyServer, 3, 3, utils.GOB, nil); err != nil {
log.Fatalf("Could not connect to history server, error: %s. Make sure you have properly configured it via -history_server flag.", err.Error())
return
} else {

View File

@@ -0,0 +1,97 @@
package main
import (
"fmt"
"log"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
type MongoMigrator struct {
session *mgo.Session
db *mgo.Database
}
func NewMongoMigrator(host, port, db, user, pass string) (*MongoMigrator, error) {
address := fmt.Sprintf("%s:%s", host, port)
if user != "" && pass != "" {
address = fmt.Sprintf("%s:%s@%s", user, pass, address)
}
session, err := mgo.Dial(address)
if err != nil {
return nil, err
}
ndb := session.DB(db)
return &MongoMigrator{session: session, db: ndb}, nil
}
func (mig MongoMigrator) migrateActions() error {
newAcsMap := make(map[string]engine.Actions)
iter := mig.db.C("actions").Find(nil).Iter()
var oldAcs struct {
Key string
Value Actions2
}
for iter.Next(&oldAcs) {
log.Printf("Migrating action: %s...", oldAcs.Key)
newAcs := make(engine.Actions, len(oldAcs.Value))
for index, oldAc := range oldAcs.Value {
a := &engine.Action{
Id: oldAc.Id,
ActionType: oldAc.ActionType,
ExtraParameters: oldAc.ExtraParameters,
ExpirationString: oldAc.ExpirationString,
Filter: oldAc.Filter,
Weight: oldAc.Weight,
Balance: &engine.BalanceFilter{
Uuid: oldAc.Balance.Uuid,
ID: oldAc.Balance.ID,
Type: oldAc.Balance.Type,
Directions: oldAc.Balance.Directions,
ExpirationDate: oldAc.Balance.ExpirationDate,
Weight: oldAc.Balance.Weight,
DestinationIDs: oldAc.Balance.DestinationIDs,
RatingSubject: oldAc.Balance.RatingSubject,
Categories: oldAc.Balance.Categories,
SharedGroups: oldAc.Balance.SharedGroups,
TimingIDs: oldAc.Balance.TimingIDs,
Timings: oldAc.Balance.Timings,
Disabled: oldAc.Balance.Disabled,
Factor: oldAc.Balance.Factor,
Blocker: oldAc.Balance.Blocker,
},
}
if oldAc.Balance.Value != nil {
a.Balance.Value = &utils.ValueFormula{Static: *oldAc.Balance.Value}
}
newAcs[index] = a
}
newAcsMap[oldAcs.Key] = newAcs
}
if err := iter.Close(); err != nil {
return err
}
// write data back
for key, acs := range newAcsMap {
if _, err := mig.db.C("actions").Upsert(bson.M{"key": key}, &struct {
Key string
Value engine.Actions
}{Key: key, Value: acs}); err != nil {
return err
}
}
return nil
}
func (mig MongoMigrator) writeVersion() error {
_, err := mig.db.C("versions").Upsert(bson.M{"key": utils.VERSION_PREFIX + "struct"}, &struct {
Key string
Value *engine.StructVersion
}{utils.VERSION_PREFIX + "struct", engine.CurrentVersion})
return err
}

View File

@@ -482,7 +482,7 @@ func (mig MigratorRC8) migrateActions() error {
bf.Type = utils.StringPointer(oldAc.BalanceType)
}
if oldAc.Balance.Value != 0 {
bf.Value = utils.Float64Pointer(oldAc.Balance.Value)
bf.Value = &utils.ValueFormula{Static: oldAc.Balance.Value}
}
if oldAc.Balance.RatingSubject != "" {
bf.RatingSubject = utils.StringPointer(oldAc.Balance.RatingSubject)
@@ -669,3 +669,11 @@ func (mig MigratorRC8) migrateSharedGroups() error {
}
return nil
}
func (mig MigratorRC8) writeVersion() error {
result, err := mig.ms.Marshal(engine.CurrentVersion)
if err != nil {
return err
}
return mig.db.Cmd("SET", utils.VERSION_PREFIX+"struct", result).Err
}

View File

@@ -364,7 +364,7 @@ func (mig MigratorRC8) migrateActionsInt() error {
bf.Type = utils.StringPointer(oldAc.BalanceType)
}
if oldAc.Balance.Value != 0 {
bf.Value = utils.Float64Pointer(oldAc.Balance.Value)
bf.Value = &utils.ValueFormula{Static: oldAc.Balance.Value}
}
if oldAc.Balance.RatingSubject != "" {
bf.RatingSubject = utils.StringPointer(oldAc.Balance.RatingSubject)

View File

@@ -0,0 +1,102 @@
package main
import (
"log"
"time"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
type BalanceFilter2 struct {
Uuid *string
ID *string
Type *string
Value *float64
Directions *utils.StringMap
ExpirationDate *time.Time
Weight *float64
DestinationIDs *utils.StringMap
RatingSubject *string
Categories *utils.StringMap
SharedGroups *utils.StringMap
TimingIDs *utils.StringMap
Timings []*engine.RITiming
Disabled *bool
Factor *engine.ValueFactor
Blocker *bool
}
type Action2 struct {
Id string
ActionType string
ExtraParameters string
Filter string
ExpirationString string // must stay as string because it can have relative values like 1month
Weight float64
Balance *BalanceFilter2
}
type Actions2 []*Action2
func (mig MigratorRC8) migrateActionsInt2() error {
keys, err := mig.db.Cmd("KEYS", utils.ACTION_PREFIX+"*").List()
if err != nil {
return err
}
newAcsMap := make(map[string]engine.Actions, len(keys))
for _, key := range keys {
log.Printf("Migrating action: %s...", key)
var oldAcs Actions2
var values []byte
if values, err = mig.db.Cmd("GET", key).Bytes(); err == nil {
if err := mig.ms.Unmarshal(values, &oldAcs); err != nil {
return err
}
}
newAcs := make(engine.Actions, len(oldAcs))
for index, oldAc := range oldAcs {
a := &engine.Action{
Id: oldAc.Id,
ActionType: oldAc.ActionType,
ExtraParameters: oldAc.ExtraParameters,
ExpirationString: oldAc.ExpirationString,
Filter: oldAc.Filter,
Weight: oldAc.Weight,
Balance: &engine.BalanceFilter{
Uuid: oldAc.Balance.Uuid,
ID: oldAc.Balance.ID,
Type: oldAc.Balance.Type,
Directions: oldAc.Balance.Directions,
ExpirationDate: oldAc.Balance.ExpirationDate,
Weight: oldAc.Balance.Weight,
DestinationIDs: oldAc.Balance.DestinationIDs,
RatingSubject: oldAc.Balance.RatingSubject,
Categories: oldAc.Balance.Categories,
SharedGroups: oldAc.Balance.SharedGroups,
TimingIDs: oldAc.Balance.TimingIDs,
Timings: oldAc.Balance.Timings,
Disabled: oldAc.Balance.Disabled,
Factor: oldAc.Balance.Factor,
Blocker: oldAc.Balance.Blocker,
},
}
if oldAc.Balance.Value != nil {
a.Balance.Value = &utils.ValueFormula{Static: *oldAc.Balance.Value}
}
newAcs[index] = a
}
newAcsMap[key] = newAcs
}
// write data back
for key, acs := range newAcsMap {
result, err := mig.ms.Marshal(&acs)
if err != nil {
return err
}
if err = mig.db.Cmd("SET", key, result).Err; err != nil {
return err
}
}
return nil
}

View File

@@ -153,7 +153,6 @@ func durRemoteRater(cd *engine.CallDescriptor) (time.Duration, error) {
func main() {
flag.Parse()
runtime.GOMAXPROCS(runtime.NumCPU() - 1)
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)

View File

@@ -0,0 +1,44 @@
package main
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net/http"
"sync"
)
func main() {
log.Print("Start!")
var wg sync.WaitGroup
for i := 1; i < 1002; i++ {
go func(index int) {
wg.Add(1)
resp, err := http.Post("http://localhost:2080/jsonrpc", "application/json", bytes.NewBuffer([]byte(fmt.Sprintf(`{"method": "ApierV1.SetAccount","params": [{"Tenant":"reglo","Account":"100%d","ActionPlanId":"PACKAGE_NEW_FOR795", "ReloadScheduler":false}], "id":%d}`, index, index))))
if err != nil {
log.Print("Post error: ", err)
}
contents, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Print("Body error: ", err)
}
log.Printf("SetAccount(%d): %s", index, string(contents))
wg.Done()
}(i)
}
wg.Wait()
for index := 1; index < 1002; index++ {
resp, err := http.Post("http://localhost:2080/jsonrpc", "application/json", bytes.NewBuffer([]byte(fmt.Sprintf(`{"method": "ApierV1.GetAccountActionPlan","params": [{"Tenant":"reglo","Account":"100%d"}], "id":%d}`, index, index))))
if err != nil {
log.Print("Post error: ", err)
}
contents, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Print("Body error: ", err)
}
log.Printf("GetAccountActionPlan(%d): %s", index, string(contents))
}
log.Print("Done!")
}

View File

@@ -25,9 +25,10 @@ import (
)
type CdrcConfig struct {
ID string // free-form text identifying this CDRC instance
Enabled bool // Enable/Disable the profile
DryRun bool // Do not post CDRs to the server
Cdrs string // The address where CDRs can be reached
CdrsConns []*HaPoolConfig // The address where CDRs can be reached
CdrFormat string // The type of CDR file to process <csv|opensips_flatstore>
FieldSeparator rune // The separator to use when reading csvs
DataUsageMultiplyFactor float64 // Conversion factor for data usage
@@ -51,14 +52,21 @@ func (self *CdrcConfig) loadFromJsonCfg(jsnCfg *CdrcJsonCfg) error {
return nil
}
var err error
if jsnCfg.Id != nil {
self.ID = *jsnCfg.Id
}
if jsnCfg.Enabled != nil {
self.Enabled = *jsnCfg.Enabled
}
if jsnCfg.Dry_run != nil {
self.DryRun = *jsnCfg.Dry_run
}
if jsnCfg.Cdrs != nil {
self.Cdrs = *jsnCfg.Cdrs
if jsnCfg.Cdrs_conns != nil {
self.CdrsConns = make([]*HaPoolConfig, len(*jsnCfg.Cdrs_conns))
for idx, jsnHaCfg := range *jsnCfg.Cdrs_conns {
self.CdrsConns[idx] = NewDfltHaPoolConfig()
self.CdrsConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnCfg.Cdr_format != nil {
self.CdrFormat = *jsnCfg.Cdr_format
@@ -125,8 +133,13 @@ func (self *CdrcConfig) loadFromJsonCfg(jsnCfg *CdrcJsonCfg) error {
// Clone itself into a new CdrcConfig
func (self *CdrcConfig) Clone() *CdrcConfig {
clnCdrc := new(CdrcConfig)
clnCdrc.ID = self.ID
clnCdrc.Enabled = self.Enabled
clnCdrc.Cdrs = self.Cdrs
clnCdrc.CdrsConns = make([]*HaPoolConfig, len(self.CdrsConns))
for idx, cdrConn := range self.CdrsConns {
clonedVal := *cdrConn
clnCdrc.CdrsConns[idx] = &clonedVal
}
clnCdrc.CdrFormat = self.CdrFormat
clnCdrc.FieldSeparator = self.FieldSeparator
clnCdrc.DataUsageMultiplyFactor = self.DataUsageMultiplyFactor

View File

@@ -7,25 +7,27 @@
// This is what you get when you load CGRateS with an empty configuration file.
"general": {
"default_reqtype": "*pseudoprepaid", // default request type to consider when missing from requests: <""|*prepaid|*postpaid|*pseudoprepaid|*rated>
"default_request_type": "*pseudoprepaid", // default request type to consider when missing from requests: <""|*prepaid|*postpaid|*pseudoprepaid|*rated>
},
"cdrs": {
"enabled": true, // start the CDR Server service: <true|false>
},
"rater": {
"rals": {
"enabled": true, // enable Rater service: <true|false>
},
"cdrc": {
"CDRC-CSV1": {
"cdrc": [
{
"id": "CDRC-CSV1",
"enabled": true, // enable CDR client functionality
"cdr_in_dir": "/tmp/cgrates/cdrc1/in", // absolute path towards the directory where the CDRs are stored
"cdr_out_dir": "/tmp/cgrates/cdrc1/out", // absolute path towards the directory where processed CDRs will be moved
"cdr_source_id": "csv1", // free form field, tag identifying the source of the CDRs within CDRS database
},
"CDRC-CSV2": {
{
"id": "CDRC-CSV2",
"enabled": true, // enable CDR client functionality
"cdr_in_dir": "/tmp/cgrates/cdrc2/in", // absolute path towards the directory where the CDRs are stored
"cdr_out_dir": "/tmp/cgrates/cdrc2/out", // absolute path towards the directory where processed CDRs will be moved
@@ -38,13 +40,13 @@
{"field_id": "Usage", "value": "~9:s/^(\\d+)$/${1}s/"},
],
},
},
],
"sm_freeswitch": {
"enabled": true, // starts SessionManager service: <true|false>
"connections":[ // instantiate connections to multiple FreeSWITCH servers
{"server": "1.2.3.4:8021", "password": "ClueCon", "reconnects": 5},
{"server": "2.3.4.5:8021", "password": "ClueCon", "reconnects": 5},
"event_socket_conns":[ // instantiate connections to multiple FreeSWITCH servers
{"address": "1.2.3.4:8021", "password": "ClueCon", "reconnects": 5},
{"address": "2.3.4.5:8021", "password": "ClueCon", "reconnects": 5},
],
},

View File

@@ -1,29 +1,19 @@
{
"cdrc": {
"CDRC-CSV2": {
"enabled": true, // enable CDR client functionality
"cdr_in_dir": "/tmp/cgrates/cdrc2/in", // absolute path towards the directory where the CDRs are stored
"cdr_out_dir": "/tmp/cgrates/cdrc2/out", // absolute path towards the directory where processed CDRs will be moved
"data_usage_multiply_factor": 0.000976563,
"cdr_source_id": "csv2", // free form field, tag identifying the source of the CDRs within CDRS database
"content_fields":[ // import template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value
{"field_id": "ToR", "value": "~7:s/^(voice|data|sms|generic)$/*$1/"},
{"field_id": "AnswerTime", "value": "2"},
],
},
"CDRC-CSV3": {
"cdrc": [
{
"id": "CDRC-CSV3",
"enabled": true, // enable CDR client functionality
"cdr_in_dir": "/tmp/cgrates/cdrc3/in", // absolute path towards the directory where the CDRs are stored
"cdr_out_dir": "/tmp/cgrates/cdrc3/out", // absolute path towards the directory where processed CDRs will be moved
"cdr_source_id": "csv3", // free form field, tag identifying the source of the CDRs within CDRS database
},
},
],
"sm_freeswitch": {
"enabled": true, // starts SessionManager service: <true|false>
"connections":[ // instantiate connections to multiple FreeSWITCH servers
{"server": "2.3.4.5:8021", "password": "ClueCon", "reconnects": 5},
"event_socket_conns":[ // instantiate connections to multiple FreeSWITCH servers
{"address": "2.3.4.5:8021", "password": "ClueCon", "reconnects": 5},
],
},

View File

@@ -60,6 +60,7 @@ func SetCgrConfig(cfg *CGRConfig) {
func NewDefaultCGRConfig() (*CGRConfig, error) {
cfg := new(CGRConfig)
cfg.InstanceID = utils.GenUUID()
cfg.DataFolderPath = "/usr/share/cgrates/"
cfg.SmGenericConfig = new(SmGenericConfig)
cfg.SmFsConfig = new(SmFsConfig)
@@ -84,9 +85,9 @@ func NewDefaultCGRConfig() (*CGRConfig, error) {
return nil, err
}
cfg.dfltCdreProfile = cfg.CdreProfiles[utils.META_DEFAULT].Clone() // So default will stay unique, will have nil pointer in case of no defaults loaded which is an extra check
cfg.dfltCdrcProfile = cfg.CdrcProfiles["/var/log/cgrates/cdrc/in"][utils.META_DEFAULT].Clone()
dfltFsConnConfig = cfg.SmFsConfig.Connections[0] // We leave it crashing here on purpose if no Connection defaults defined
dfltKamConnConfig = cfg.SmKamConfig.Connections[0]
cfg.dfltCdrcProfile = cfg.CdrcProfiles["/var/log/cgrates/cdrc/in"][0].Clone()
dfltFsConnConfig = cfg.SmFsConfig.EventSocketConns[0] // We leave it crashing here on purpose if no Connection defaults defined
dfltKamConnConfig = cfg.SmKamConfig.EvapiConns[0]
if err := cfg.checkConfigSanity(); err != nil {
return nil, err
}
@@ -167,89 +168,90 @@ func NewCGRConfigFromFolder(cfgDir string) (*CGRConfig, error) {
// Holds system configuration, defaults are overwritten with values from config file if found
type CGRConfig struct {
TpDbType string
TpDbHost string // The host to connect to. Values that start with / are for UNIX domain sockets.
TpDbPort string // The port to bind to.
TpDbName string // The name of the database to connect to.
TpDbUser string // The user to sign in as.
TpDbPass string // The user's password.
DataDbType string
DataDbHost string // The host to connect to. Values that start with / are for UNIX domain sockets.
DataDbPort string // The port to bind to.
DataDbName string // The name of the database to connect to.
DataDbUser string // The user to sign in as.
DataDbPass string // The user's password.
LoadHistorySize int // Maximum number of records to archive in load history
StorDBType string // Should reflect the database type used to store logs
StorDBHost string // The host to connect to. Values that start with / are for UNIX domain sockets.
StorDBPort string // Th e port to bind to.
StorDBName string // The name of the database to connect to.
StorDBUser string // The user to sign in as.
StorDBPass string // The user's password.
StorDBMaxOpenConns int // Maximum database connections opened
StorDBMaxIdleConns int // Maximum idle connections to keep opened
StorDBCDRSIndexes []string
DBDataEncoding string // The encoding used to store object data in strings: <msgpack|json>
RPCJSONListen string // RPC JSON listening address
RPCGOBListen string // RPC GOB listening address
HTTPListen string // HTTP listening address
DefaultReqType string // Use this request type if not defined on top
DefaultCategory string // set default type of record
DefaultTenant string // set default tenant
DefaultSubject string // set default rating subject, useful in case of fallback
DefaultTimezone string // default timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB>
Reconnects int // number of recconect attempts in case of connection lost <-1 for infinite | nb>
ConnectAttempts int // number of initial connection attempts before giving up
ResponseCacheTTL time.Duration // the life span of a cached response
InternalTtl time.Duration // maximum duration to wait for internal connections before giving up
RoundingDecimals int // Number of decimals to round end prices at
HttpSkipTlsVerify bool // If enabled Http Client will accept any TLS certificate
TpExportPath string // Path towards export folder for offline Tariff Plans
HttpFailedDir string // Directory path where we store failed http requests
MaxCallDuration time.Duration // The maximum call duration (used by responder when querying DerivedCharging) // ToDo: export it in configuration file
RaterEnabled bool // start standalone server (no balancer)
RaterBalancer string // balancer address host:port
RaterCdrStats string // address where to reach the cdrstats service. Empty to disable stats gathering <""|internal|x.y.z.y:1234>
RaterHistoryServer string
RaterPubSubServer string
RaterUserServer string
RaterAliasesServer string
RpSubjectPrefixMatching bool // enables prefix matching for the rating profile subject
BalancerEnabled bool
SchedulerEnabled bool
CDRSEnabled bool // Enable CDR Server service
CDRSExtraFields []*utils.RSRField // Extra fields to store in CDRs
CDRSStoreCdrs bool // store cdrs in storDb
CDRSRater string // address where to reach the Rater for cost calculation: <""|internal|x.y.z.y:1234>
CDRSPubSub string // address where to reach the pubsub service: <""|internal|x.y.z.y:1234>
CDRSUsers string // address where to reach the users service: <""|internal|x.y.z.y:1234>
CDRSAliases string // address where to reach the aliases service: <""|internal|x.y.z.y:1234>
CDRSStats string // address where to reach the cdrstats service. Empty to disable stats gathering <""|internal|x.y.z.y:1234>
CDRSCdrReplication []*CdrReplicationCfg // Replicate raw CDRs to a number of servers
CDRStatsEnabled bool // Enable CDR Stats service
CDRStatsSaveInterval time.Duration // Save interval duration
CdreProfiles map[string]*CdreConfig
CdrcProfiles map[string]map[string]*CdrcConfig // Number of CDRC instances running imports, format map[dirPath]map[instanceName]{Configs}
SmGenericConfig *SmGenericConfig
SmFsConfig *SmFsConfig // SM-FreeSWITCH configuration
SmKamConfig *SmKamConfig // SM-Kamailio Configuration
SmOsipsConfig *SmOsipsConfig // SM-OpenSIPS Configuration
diameterAgentCfg *DiameterAgentCfg // DiameterAgent configuration
HistoryServer string // Address where to reach the master history server: <internal|x.y.z.y:1234>
HistoryServerEnabled bool // Starts History as server: <true|false>.
HistoryDir string // Location on disk where to store history files.
HistorySaveInterval time.Duration // The timout duration between pubsub writes
PubSubServerEnabled bool // Starts PubSub as server: <true|false>.
AliasesServerEnabled bool // Starts PubSub as server: <true|false>.
UserServerEnabled bool // Starts User as server: <true|false>
UserServerIndexes []string // List of user profile field indexes
MailerServer string // The server to use when sending emails out
MailerAuthUser string // Authenticate to email server using this user
MailerAuthPass string // Authenticate to email server with this password
MailerFromAddr string // From address used when sending emails out
DataFolderPath string // Path towards data folder, for tests internal usage, not loading out of .json options
sureTaxCfg *SureTaxCfg // Load here SureTax configuration, as pointer so we can have runtime reloads in the future
ConfigReloads map[string]chan struct{} // Signals to specific entities that a config reload should occur
InstanceID string // Identifier for this engine instance
TpDbType string
TpDbHost string // The host to connect to. Values that start with / are for UNIX domain sockets.
TpDbPort string // The port to bind to.
TpDbName string // The name of the database to connect to.
TpDbUser string // The user to sign in as.
TpDbPass string // The user's password.
DataDbType string
DataDbHost string // The host to connect to. Values that start with / are for UNIX domain sockets.
DataDbPort string // The port to bind to.
DataDbName string // The name of the database to connect to.
DataDbUser string // The user to sign in as.
DataDbPass string // The user's password.
LoadHistorySize int // Maximum number of records to archive in load history
StorDBType string // Should reflect the database type used to store logs
StorDBHost string // The host to connect to. Values that start with / are for UNIX domain sockets.
StorDBPort string // Th e port to bind to.
StorDBName string // The name of the database to connect to.
StorDBUser string // The user to sign in as.
StorDBPass string // The user's password.
StorDBMaxOpenConns int // Maximum database connections opened
StorDBMaxIdleConns int // Maximum idle connections to keep opened
StorDBCDRSIndexes []string
DBDataEncoding string // The encoding used to store object data in strings: <msgpack|json>
RPCJSONListen string // RPC JSON listening address
RPCGOBListen string // RPC GOB listening address
HTTPListen string // HTTP listening address
DefaultReqType string // Use this request type if not defined on top
DefaultCategory string // set default type of record
DefaultTenant string // set default tenant
DefaultTimezone string // default timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB>
Reconnects int // number of recconect attempts in case of connection lost <-1 for infinite | nb>
ConnectAttempts int // number of initial connection attempts before giving up
ResponseCacheTTL time.Duration // the life span of a cached response
InternalTtl time.Duration // maximum duration to wait for internal connections before giving up
RoundingDecimals int // Number of decimals to round end prices at
HttpSkipTlsVerify bool // If enabled Http Client will accept any TLS certificate
TpExportPath string // Path towards export folder for offline Tariff Plans
HttpFailedDir string // Directory path where we store failed http requests
MaxCallDuration time.Duration // The maximum call duration (used by responder when querying DerivedCharging) // ToDo: export it in configuration file
RALsEnabled bool // start standalone server (no balancer)
RALsBalancer string // balancer address host:port
RALsCDRStatSConns []*HaPoolConfig // address where to reach the cdrstats service. Empty to disable stats gathering <""|internal|x.y.z.y:1234>
RALsHistorySConns []*HaPoolConfig
RALsPubSubSConns []*HaPoolConfig
RALsUserSConns []*HaPoolConfig
RALsAliasSConns []*HaPoolConfig
RpSubjectPrefixMatching bool // enables prefix matching for the rating profile subject
LcrSubjectPrefixMatching bool // enables prefix matching for the lcr subject
BalancerEnabled bool
SchedulerEnabled bool
CDRSEnabled bool // Enable CDR Server service
CDRSExtraFields []*utils.RSRField // Extra fields to store in CDRs
CDRSStoreCdrs bool // store cdrs in storDb
CDRSRaterConns []*HaPoolConfig // address where to reach the Rater for cost calculation: <""|internal|x.y.z.y:1234>
CDRSPubSubSConns []*HaPoolConfig // address where to reach the pubsub service: <""|internal|x.y.z.y:1234>
CDRSUserSConns []*HaPoolConfig // address where to reach the users service: <""|internal|x.y.z.y:1234>
CDRSAliaseSConns []*HaPoolConfig // address where to reach the aliases service: <""|internal|x.y.z.y:1234>
CDRSStatSConns []*HaPoolConfig // address where to reach the cdrstats service. Empty to disable stats gathering <""|internal|x.y.z.y:1234>
CDRSCdrReplication []*CdrReplicationCfg // Replicate raw CDRs to a number of servers
CDRStatsEnabled bool // Enable CDR Stats service
CDRStatsSaveInterval time.Duration // Save interval duration
CdreProfiles map[string]*CdreConfig
CdrcProfiles map[string][]*CdrcConfig // Number of CDRC instances running imports, format map[dirPath][]{Configs}
SmGenericConfig *SmGenericConfig
SmFsConfig *SmFsConfig // SMFreeSWITCH configuration
SmKamConfig *SmKamConfig // SM-Kamailio Configuration
SmOsipsConfig *SmOsipsConfig // SMOpenSIPS Configuration
diameterAgentCfg *DiameterAgentCfg // DiameterAgent configuration
HistoryServer string // Address where to reach the master history server: <internal|x.y.z.y:1234>
HistoryServerEnabled bool // Starts History as server: <true|false>.
HistoryDir string // Location on disk where to store history files.
HistorySaveInterval time.Duration // The timout duration between pubsub writes
PubSubServerEnabled bool // Starts PubSub as server: <true|false>.
AliasesServerEnabled bool // Starts PubSub as server: <true|false>.
UserServerEnabled bool // Starts User as server: <true|false>
UserServerIndexes []string // List of user profile field indexes
MailerServer string // The server to use when sending emails out
MailerAuthUser string // Authenticate to email server using this user
MailerAuthPass string // Authenticate to email server with this password
MailerFromAddr string // From address used when sending emails out
DataFolderPath string // Path towards data folder, for tests internal usage, not loading out of .json options
sureTaxCfg *SureTaxCfg // Load here SureTax configuration, as pointer so we can have runtime reloads in the future
ConfigReloads map[string]chan struct{} // Signals to specific entities that a config reload should occur
// Cache defaults loaded from json and needing clones
dfltCdreProfile *CdreConfig // Default cdreConfig profile
dfltCdrcProfile *CdrcConfig // Default cdrcConfig profile
@@ -257,42 +259,62 @@ type CGRConfig struct {
func (self *CGRConfig) checkConfigSanity() error {
// Rater checks
if self.RaterEnabled {
if self.RaterBalancer == utils.INTERNAL && !self.BalancerEnabled {
if self.RALsEnabled {
if self.RALsBalancer == utils.MetaInternal && !self.BalancerEnabled {
return errors.New("Balancer not enabled but requested by Rater component.")
}
if self.RaterCdrStats == utils.INTERNAL && !self.CDRStatsEnabled {
return errors.New("CDRStats not enabled but requested by Rater component.")
for _, connCfg := range self.RALsCDRStatSConns {
if connCfg.Address == utils.MetaInternal && !self.CDRStatsEnabled {
return errors.New("CDRStats not enabled but requested by Rater component.")
}
}
if self.RaterHistoryServer == utils.INTERNAL && !self.HistoryServerEnabled {
return errors.New("History server not enabled but requested by Rater component.")
for _, connCfg := range self.RALsHistorySConns {
if connCfg.Address == utils.MetaInternal && !self.HistoryServerEnabled {
return errors.New("History server not enabled but requested by Rater component.")
}
}
if self.RaterPubSubServer == utils.INTERNAL && !self.PubSubServerEnabled {
return errors.New("PubSub server not enabled but requested by Rater component.")
for _, connCfg := range self.RALsPubSubSConns {
if connCfg.Address == utils.MetaInternal && !self.PubSubServerEnabled {
return errors.New("PubSub server not enabled but requested by Rater component.")
}
}
if self.RaterAliasesServer == utils.INTERNAL && !self.AliasesServerEnabled {
return errors.New("Aliases server not enabled but requested by Rater component.")
for _, connCfg := range self.RALsAliasSConns {
if connCfg.Address == utils.MetaInternal && !self.AliasesServerEnabled {
return errors.New("Alias server not enabled but requested by Rater component.")
}
}
if self.RaterUserServer == utils.INTERNAL && !self.UserServerEnabled {
return errors.New("Users service not enabled but requested by Rater component.")
for _, connCfg := range self.RALsUserSConns {
if connCfg.Address == utils.MetaInternal && !self.UserServerEnabled {
return errors.New("User service not enabled but requested by Rater component.")
}
}
}
// CDRServer checks
if self.CDRSEnabled {
if self.CDRSRater == utils.INTERNAL && !self.RaterEnabled {
return errors.New("Rater not enabled but requested by CDRS component.")
for _, cdrsRaterConn := range self.CDRSRaterConns {
if cdrsRaterConn.Address == utils.MetaInternal && !self.RALsEnabled {
return errors.New("RALs not enabled but requested by CDRS component.")
}
}
if self.CDRSPubSub == utils.INTERNAL && !self.PubSubServerEnabled {
return errors.New("PubSub service not enabled but requested by CDRS component.")
for _, connCfg := range self.CDRSPubSubSConns {
if connCfg.Address == utils.MetaInternal && !self.PubSubServerEnabled {
return errors.New("PubSubS not enabled but requested by CDRS component.")
}
}
if self.CDRSUsers == utils.INTERNAL && !self.UserServerEnabled {
return errors.New("Users service not enabled but requested by CDRS component.")
for _, connCfg := range self.CDRSUserSConns {
if connCfg.Address == utils.MetaInternal && !self.UserServerEnabled {
return errors.New("UserS not enabled but requested by CDRS component.")
}
}
if self.CDRSAliases == utils.INTERNAL && !self.AliasesServerEnabled {
return errors.New("Aliases service not enabled but requested by CDRS component.")
for _, connCfg := range self.CDRSAliaseSConns {
if connCfg.Address == utils.MetaInternal && !self.AliasesServerEnabled {
return errors.New("AliaseS not enabled but requested by CDRS component.")
}
}
if self.CDRSStats == utils.INTERNAL && !self.CDRStatsEnabled {
return errors.New("CDRStats not enabled but requested by CDRS component.")
for _, connCfg := range self.CDRSStatSConns {
if connCfg.Address == utils.MetaInternal && !self.CDRStatsEnabled {
return errors.New("CDRStatS not enabled but requested by CDRS component.")
}
}
}
// CDRC sanity checks
@@ -301,10 +323,13 @@ func (self *CGRConfig) checkConfigSanity() error {
if !cdrcInst.Enabled {
continue
}
if len(cdrcInst.Cdrs) == 0 {
return errors.New("CdrC enabled but no CDRS defined!")
} else if cdrcInst.Cdrs == utils.INTERNAL && !self.CDRSEnabled {
return errors.New("CDRS not enabled but referenced from CDRC")
if len(cdrcInst.CdrsConns) == 0 {
return fmt.Errorf("<CDRC> Instance: %s, CdrC enabled but no CDRS defined!", cdrcInst.ID)
}
for _, conn := range cdrcInst.CdrsConns {
if conn.Address == utils.MetaInternal && !self.CDRSEnabled {
return errors.New("CDRS not enabled but referenced from CDRC")
}
}
if len(cdrcInst.ContentFields) == 0 {
return errors.New("CdrC enabled but no fields to be processed defined!")
@@ -320,70 +345,94 @@ func (self *CGRConfig) checkConfigSanity() error {
}
}
}
// SM-Generic checks
// SMGeneric checks
if self.SmGenericConfig.Enabled {
if len(self.SmGenericConfig.HaRater) == 0 {
return errors.New("Rater definition is mandatory!")
if len(self.SmGenericConfig.RALsConns) == 0 {
return errors.New("<SMGeneric> RALs definition is mandatory!")
}
if len(self.SmGenericConfig.HaCdrs) == 0 {
return errors.New("Cdrs definition is mandatory!")
for _, smgRALsConn := range self.SmGenericConfig.RALsConns {
if smgRALsConn.Address == utils.MetaInternal && !self.RALsEnabled {
return errors.New("<SMGeneric> RALs not enabled but requested by SMGeneric component.")
}
}
if self.SmGenericConfig.HaRater[0].Server == utils.INTERNAL && !self.RaterEnabled {
return errors.New("Rater not enabled but requested by SM-Generic component.")
if len(self.SmGenericConfig.CDRsConns) == 0 {
return errors.New("<SMGeneric> CDRs definition is mandatory!")
}
if self.SmGenericConfig.HaCdrs[0].Server == utils.INTERNAL && !self.CDRSEnabled {
return errors.New("CDRS not enabled but referenced by SM-Generic component")
for _, smgCDRSConn := range self.SmGenericConfig.CDRsConns {
if smgCDRSConn.Address == utils.MetaInternal && !self.CDRSEnabled {
return errors.New("<SMGeneric> CDRS not enabled but referenced by SMGeneric component")
}
}
}
// SM-FreeSWITCH checks
// SMFreeSWITCH checks
if self.SmFsConfig.Enabled {
if len(self.SmFsConfig.HaRater) == 0 {
return errors.New("Rater definition is mandatory!")
if len(self.SmFsConfig.RALsConns) == 0 {
return errors.New("<SMFreeSWITCH> RALs definition is mandatory!")
}
if len(self.SmFsConfig.HaCdrs) == 0 {
return errors.New("Cdrs definition is mandatory!")
for _, smFSRaterConn := range self.SmFsConfig.RALsConns {
if smFSRaterConn.Address == utils.MetaInternal && !self.RALsEnabled {
return errors.New("<SMFreeSWITCH> RALs not enabled but requested by SMFreeSWITCH component.")
}
}
if self.SmFsConfig.HaRater[0].Server == utils.INTERNAL && !self.RaterEnabled {
return errors.New("Rater not enabled but requested by SM-FreeSWITCH component.")
if len(self.SmFsConfig.CDRsConns) == 0 {
return errors.New("<SMFreeSWITCH> CDRS definition is mandatory!")
}
if self.SmFsConfig.HaCdrs[0].Server == utils.INTERNAL && !self.CDRSEnabled {
return errors.New("CDRS not enabled but referenced by SM-FreeSWITCH component")
for _, smFSCDRSConn := range self.SmFsConfig.CDRsConns {
if smFSCDRSConn.Address == utils.MetaInternal && !self.CDRSEnabled {
return errors.New("CDRS not enabled but referenced by SMFreeSWITCH component")
}
}
}
// SM-Kamailio checks
if self.SmKamConfig.Enabled {
if len(self.SmKamConfig.HaRater) == 0 {
if len(self.SmKamConfig.RALsConns) == 0 {
return errors.New("Rater definition is mandatory!")
}
if len(self.SmKamConfig.HaCdrs) == 0 {
for _, smKamRaterConn := range self.SmKamConfig.RALsConns {
if smKamRaterConn.Address == utils.MetaInternal && !self.RALsEnabled {
return errors.New("Rater not enabled but requested by SM-Kamailio component.")
}
}
if len(self.SmKamConfig.CDRsConns) == 0 {
return errors.New("Cdrs definition is mandatory!")
}
if self.SmKamConfig.HaRater[0].Server == utils.INTERNAL && !self.RaterEnabled {
return errors.New("Rater not enabled but requested by SM-Kamailio component.")
}
if self.SmKamConfig.HaCdrs[0].Server == utils.INTERNAL && !self.CDRSEnabled {
return errors.New("CDRS not enabled but referenced by SM-Kamailio component")
for _, smKamCDRSConn := range self.SmKamConfig.CDRsConns {
if smKamCDRSConn.Address == utils.MetaInternal && !self.CDRSEnabled {
return errors.New("CDRS not enabled but referenced by SM-Kamailio component")
}
}
}
// SM-OpenSIPS checks
// SMOpenSIPS checks
if self.SmOsipsConfig.Enabled {
if len(self.SmOsipsConfig.HaRater) == 0 {
return errors.New("Rater definition is mandatory!")
if len(self.SmOsipsConfig.RALsConns) == 0 {
return errors.New("<SMOpenSIPS> Rater definition is mandatory!")
}
if len(self.SmOsipsConfig.HaCdrs) == 0 {
return errors.New("Cdrs definition is mandatory!")
for _, smOsipsRaterConn := range self.SmOsipsConfig.RALsConns {
if smOsipsRaterConn.Address == utils.MetaInternal && !self.RALsEnabled {
return errors.New("<SMOpenSIPS> RALs not enabled but requested by SMOpenSIPS component.")
}
}
if self.SmOsipsConfig.HaRater[0].Server == utils.INTERNAL && !self.RaterEnabled {
return errors.New("Rater not enabled but requested by SM-OpenSIPS component.")
if len(self.SmOsipsConfig.CDRsConns) == 0 {
return errors.New("<SMOpenSIPS> CDRs definition is mandatory!")
}
if self.SmOsipsConfig.HaCdrs[0].Server == utils.INTERNAL && !self.CDRSEnabled {
return errors.New("CDRS not enabled but referenced by SM-OpenSIPS component")
for _, smOsipsCDRSConn := range self.SmOsipsConfig.CDRsConns {
if smOsipsCDRSConn.Address == utils.MetaInternal && !self.CDRSEnabled {
return errors.New("<SMOpenSIPS> CDRS not enabled but referenced by SMOpenSIPS component")
}
}
}
// DAgent checks
if self.diameterAgentCfg.Enabled {
if self.diameterAgentCfg.SMGeneric == utils.INTERNAL && !self.SmGenericConfig.Enabled {
return errors.New("SMGeneric not enabled but referenced by DiameterAgent component")
for _, daSMGConn := range self.diameterAgentCfg.SMGenericConns {
if daSMGConn.Address == utils.MetaInternal && !self.SmGenericConfig.Enabled {
return errors.New("SMGeneric not enabled but referenced by DiameterAgent component")
}
}
for _, daPubSubSConn := range self.diameterAgentCfg.PubSubConns {
if daPubSubSConn.Address == utils.MetaInternal && !self.PubSubServerEnabled {
return errors.New("PubSubS not enabled but requested by DiameterAgent component.")
}
}
}
return nil
@@ -423,7 +472,7 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) error {
return err
}
jsnRaterCfg, err := jsnCfg.RaterJsonCfg()
jsnRALsCfg, err := jsnCfg.RalsJsonCfg()
if err != nil {
return err
}
@@ -525,8 +574,8 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) error {
if jsnTpDbCfg.Db_user != nil {
self.TpDbUser = *jsnTpDbCfg.Db_user
}
if jsnTpDbCfg.Db_passwd != nil {
self.TpDbPass = *jsnTpDbCfg.Db_passwd
if jsnTpDbCfg.Db_password != nil {
self.TpDbPass = *jsnTpDbCfg.Db_password
}
}
@@ -546,8 +595,8 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) error {
if jsnDataDbCfg.Db_user != nil {
self.DataDbUser = *jsnDataDbCfg.Db_user
}
if jsnDataDbCfg.Db_passwd != nil {
self.DataDbPass = *jsnDataDbCfg.Db_passwd
if jsnDataDbCfg.Db_password != nil {
self.DataDbPass = *jsnDataDbCfg.Db_password
}
if jsnDataDbCfg.Load_history_size != nil {
self.LoadHistorySize = *jsnDataDbCfg.Load_history_size
@@ -570,8 +619,8 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) error {
if jsnStorDbCfg.Db_user != nil {
self.StorDBUser = *jsnStorDbCfg.Db_user
}
if jsnStorDbCfg.Db_passwd != nil {
self.StorDBPass = *jsnStorDbCfg.Db_passwd
if jsnStorDbCfg.Db_password != nil {
self.StorDBPass = *jsnStorDbCfg.Db_password
}
if jsnStorDbCfg.Max_open_conns != nil {
self.StorDBMaxOpenConns = *jsnStorDbCfg.Max_open_conns
@@ -588,8 +637,8 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) error {
if jsnGeneralCfg.Dbdata_encoding != nil {
self.DBDataEncoding = *jsnGeneralCfg.Dbdata_encoding
}
if jsnGeneralCfg.Default_reqtype != nil {
self.DefaultReqType = *jsnGeneralCfg.Default_reqtype
if jsnGeneralCfg.Default_request_type != nil {
self.DefaultReqType = *jsnGeneralCfg.Default_request_type
}
if jsnGeneralCfg.Default_category != nil {
self.DefaultCategory = *jsnGeneralCfg.Default_category
@@ -597,9 +646,6 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) error {
if jsnGeneralCfg.Default_tenant != nil {
self.DefaultTenant = *jsnGeneralCfg.Default_tenant
}
if jsnGeneralCfg.Default_subject != nil {
self.DefaultSubject = *jsnGeneralCfg.Default_subject
}
if jsnGeneralCfg.Connect_attempts != nil {
self.ConnectAttempts = *jsnGeneralCfg.Connect_attempts
}
@@ -645,30 +691,53 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) error {
}
}
if jsnRaterCfg != nil {
if jsnRaterCfg.Enabled != nil {
self.RaterEnabled = *jsnRaterCfg.Enabled
if jsnRALsCfg != nil {
if jsnRALsCfg.Enabled != nil {
self.RALsEnabled = *jsnRALsCfg.Enabled
}
if jsnRaterCfg.Balancer != nil {
self.RaterBalancer = *jsnRaterCfg.Balancer
if jsnRALsCfg.Balancer != nil {
self.RALsBalancer = *jsnRALsCfg.Balancer
}
if jsnRaterCfg.Cdrstats != nil {
self.RaterCdrStats = *jsnRaterCfg.Cdrstats
if jsnRALsCfg.Cdrstats_conns != nil {
self.RALsCDRStatSConns = make([]*HaPoolConfig, len(*jsnRALsCfg.Cdrstats_conns))
for idx, jsnHaCfg := range *jsnRALsCfg.Cdrstats_conns {
self.RALsCDRStatSConns[idx] = NewDfltHaPoolConfig()
self.RALsCDRStatSConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnRaterCfg.Historys != nil {
self.RaterHistoryServer = *jsnRaterCfg.Historys
if jsnRALsCfg.Historys_conns != nil {
self.RALsHistorySConns = make([]*HaPoolConfig, len(*jsnRALsCfg.Historys_conns))
for idx, jsnHaCfg := range *jsnRALsCfg.Historys_conns {
self.RALsHistorySConns[idx] = NewDfltHaPoolConfig()
self.RALsHistorySConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnRaterCfg.Pubsubs != nil {
self.RaterPubSubServer = *jsnRaterCfg.Pubsubs
if jsnRALsCfg.Pubsubs_conns != nil {
self.RALsPubSubSConns = make([]*HaPoolConfig, len(*jsnRALsCfg.Pubsubs_conns))
for idx, jsnHaCfg := range *jsnRALsCfg.Pubsubs_conns {
self.RALsPubSubSConns[idx] = NewDfltHaPoolConfig()
self.RALsPubSubSConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnRaterCfg.Aliases != nil {
self.RaterAliasesServer = *jsnRaterCfg.Aliases
if jsnRALsCfg.Aliases_conns != nil {
self.RALsAliasSConns = make([]*HaPoolConfig, len(*jsnRALsCfg.Aliases_conns))
for idx, jsnHaCfg := range *jsnRALsCfg.Aliases_conns {
self.RALsAliasSConns[idx] = NewDfltHaPoolConfig()
self.RALsAliasSConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnRaterCfg.Users != nil {
self.RaterUserServer = *jsnRaterCfg.Users
if jsnRALsCfg.Users_conns != nil {
self.RALsUserSConns = make([]*HaPoolConfig, len(*jsnRALsCfg.Users_conns))
for idx, jsnHaCfg := range *jsnRALsCfg.Users_conns {
self.RALsUserSConns[idx] = NewDfltHaPoolConfig()
self.RALsUserSConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnRaterCfg.Rp_subject_prefix_matching != nil {
self.RpSubjectPrefixMatching = *jsnRaterCfg.Rp_subject_prefix_matching
if jsnRALsCfg.Rp_subject_prefix_matching != nil {
self.RpSubjectPrefixMatching = *jsnRALsCfg.Rp_subject_prefix_matching
}
if jsnRALsCfg.Lcr_subject_prefix_matching != nil {
self.LcrSubjectPrefixMatching = *jsnRALsCfg.Lcr_subject_prefix_matching
}
}
@@ -692,20 +761,40 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) error {
if jsnCdrsCfg.Store_cdrs != nil {
self.CDRSStoreCdrs = *jsnCdrsCfg.Store_cdrs
}
if jsnCdrsCfg.Rater != nil {
self.CDRSRater = *jsnCdrsCfg.Rater
if jsnCdrsCfg.Rals_conns != nil {
self.CDRSRaterConns = make([]*HaPoolConfig, len(*jsnCdrsCfg.Rals_conns))
for idx, jsnHaCfg := range *jsnCdrsCfg.Rals_conns {
self.CDRSRaterConns[idx] = NewDfltHaPoolConfig()
self.CDRSRaterConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnCdrsCfg.Pubsubs != nil {
self.CDRSPubSub = *jsnCdrsCfg.Pubsubs
if jsnCdrsCfg.Pubsubs_conns != nil {
self.CDRSPubSubSConns = make([]*HaPoolConfig, len(*jsnCdrsCfg.Pubsubs_conns))
for idx, jsnHaCfg := range *jsnCdrsCfg.Pubsubs_conns {
self.CDRSPubSubSConns[idx] = NewDfltHaPoolConfig()
self.CDRSPubSubSConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnCdrsCfg.Users != nil {
self.CDRSUsers = *jsnCdrsCfg.Users
if jsnCdrsCfg.Users_conns != nil {
self.CDRSUserSConns = make([]*HaPoolConfig, len(*jsnCdrsCfg.Users_conns))
for idx, jsnHaCfg := range *jsnCdrsCfg.Users_conns {
self.CDRSUserSConns[idx] = NewDfltHaPoolConfig()
self.CDRSUserSConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnCdrsCfg.Aliases != nil {
self.CDRSAliases = *jsnCdrsCfg.Aliases
if jsnCdrsCfg.Aliases_conns != nil {
self.CDRSAliaseSConns = make([]*HaPoolConfig, len(*jsnCdrsCfg.Aliases_conns))
for idx, jsnHaCfg := range *jsnCdrsCfg.Aliases_conns {
self.CDRSAliaseSConns[idx] = NewDfltHaPoolConfig()
self.CDRSAliaseSConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnCdrsCfg.Cdrstats != nil {
self.CDRSStats = *jsnCdrsCfg.Cdrstats
if jsnCdrsCfg.Cdrstats_conns != nil {
self.CDRSStatSConns = make([]*HaPoolConfig, len(*jsnCdrsCfg.Cdrstats_conns))
for idx, jsnHaCfg := range *jsnCdrsCfg.Cdrstats_conns {
self.CDRSStatSConns[idx] = NewDfltHaPoolConfig()
self.CDRSStatSConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnCdrsCfg.Cdr_replication != nil {
self.CDRSCdrReplication = make([]*CdrReplicationCfg, len(*jsnCdrsCfg.Cdr_replication))
@@ -714,8 +803,8 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) error {
if rplJsonCfg.Transport != nil {
self.CDRSCdrReplication[idx].Transport = *rplJsonCfg.Transport
}
if rplJsonCfg.Server != nil {
self.CDRSCdrReplication[idx].Server = *rplJsonCfg.Server
if rplJsonCfg.Address != nil {
self.CDRSCdrReplication[idx].Address = *rplJsonCfg.Address
}
if rplJsonCfg.Synchronous != nil {
self.CDRSCdrReplication[idx].Synchronous = *rplJsonCfg.Synchronous
@@ -760,28 +849,26 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) error {
}
}
}
if jsnCdrcCfg != nil {
if self.CdrcProfiles == nil {
self.CdrcProfiles = make(map[string]map[string]*CdrcConfig)
self.CdrcProfiles = make(map[string][]*CdrcConfig)
}
for profileName, jsnCrc1Cfg := range jsnCdrcCfg {
for _, jsnCrc1Cfg := range jsnCdrcCfg {
if _, hasDir := self.CdrcProfiles[*jsnCrc1Cfg.Cdr_in_dir]; !hasDir {
self.CdrcProfiles[*jsnCrc1Cfg.Cdr_in_dir] = make(map[string]*CdrcConfig)
self.CdrcProfiles[*jsnCrc1Cfg.Cdr_in_dir] = make([]*CdrcConfig, 0)
}
if _, hasProfile := self.CdrcProfiles[profileName]; !hasProfile {
if profileName == utils.META_DEFAULT {
self.CdrcProfiles[*jsnCrc1Cfg.Cdr_in_dir][profileName] = new(CdrcConfig)
} else {
self.CdrcProfiles[*jsnCrc1Cfg.Cdr_in_dir][profileName] = self.dfltCdrcProfile.Clone() // Clone default so we do not inherit pointers
}
var cdrcInstCfg *CdrcConfig
if *jsnCrc1Cfg.Id == utils.META_DEFAULT && self.dfltCdrcProfile == nil {
cdrcInstCfg = new(CdrcConfig)
} else {
cdrcInstCfg = self.dfltCdrcProfile.Clone() // Clone default so we do not inherit pointers
}
if err = self.CdrcProfiles[*jsnCrc1Cfg.Cdr_in_dir][profileName].loadFromJsonCfg(jsnCrc1Cfg); err != nil {
if err := cdrcInstCfg.loadFromJsonCfg(jsnCrc1Cfg); err != nil {
return err
}
self.CdrcProfiles[*jsnCrc1Cfg.Cdr_in_dir] = append(self.CdrcProfiles[*jsnCrc1Cfg.Cdr_in_dir], cdrcInstCfg)
}
}
if jsnSmGenericCfg != nil {
if err := self.SmGenericConfig.loadFromJsonCfg(jsnSmGenericCfg); err != nil {
return err
@@ -853,8 +940,8 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) error {
if jsnMailerCfg.Auth_user != nil {
self.MailerAuthUser = *jsnMailerCfg.Auth_user
}
if jsnMailerCfg.Auth_passwd != nil {
self.MailerAuthPass = *jsnMailerCfg.Auth_passwd
if jsnMailerCfg.Auth_password != nil {
self.MailerAuthPass = *jsnMailerCfg.Auth_password
}
if jsnMailerCfg.From_address != nil {
self.MailerFromAddr = *jsnMailerCfg.From_address

View File

@@ -29,18 +29,17 @@ const CGRATES_CFG_JSON = `
"general": {
"http_skip_tls_verify": false, // if enabled Http Client will accept any TLS certificate
"rounding_decimals": 5, // system level precision for floats
"rounding_decimals": 5, // system level precision for floats
"dbdata_encoding": "msgpack", // encoding used to store object data in strings: <msgpack|json>
"tpexport_dir": "/var/log/cgrates/tpe", // path towards export folder for offline Tariff Plans
"http_failed_dir": "/var/log/cgrates/http_failed", // directory path where we store failed http requests
"default_reqtype": "*rated", // default request type to consider when missing from requests: <""|*prepaid|*postpaid|*pseudoprepaid|*rated>
"default_category": "call", // default Type of Record to consider when missing from requests
"default_tenant": "cgrates.org", // default Tenant to consider when missing from requests
"default_subject": "cgrates", // default rating Subject to consider when missing from requests
"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
"response_cache_ttl": "3s", // the life span of a cached response
"response_cache_ttl": "0s", // the life span of a cached response
"internal_ttl": "2m", // maximum duration to wait for internal connections before giving up
},
@@ -58,7 +57,7 @@ const CGRATES_CFG_JSON = `
"db_port": 6379, // port to reach the tariffplan_db
"db_name": "10", // tariffplan_db name to connect to
"db_user": "", // sername to use when connecting to tariffplan_db
"db_passwd": "", // password to use when connecting to tariffplan_db
"db_password": "", // password to use when connecting to tariffplan_db
},
@@ -68,7 +67,7 @@ const CGRATES_CFG_JSON = `
"db_port": 6379, // data_db port to reach the database
"db_name": "11", // data_db database name to connect to
"db_user": "", // username to use when connecting to data_db
"db_passwd": "", // password to use when connecting to data_db
"db_password": "", // password to use when connecting to data_db
"load_history_size": 10, // Number of records in the load history
},
@@ -79,7 +78,7 @@ const CGRATES_CFG_JSON = `
"db_port": 3306, // the port to reach the stordb
"db_name": "cgrates", // stor database name
"db_user": "cgrates", // username to use when connecting to stordb
"db_passwd": "CGRateS.org", // password to use when connecting to stordb
"db_password": "CGRateS.org", // password to use when connecting to stordb
"max_open_conns": 100, // maximum database connections opened
"max_idle_conns": 10, // maximum database connections idle
"cdrs_indexes": [], // indexes on cdrs table to speed up queries, used only in case of mongo
@@ -91,15 +90,16 @@ const CGRATES_CFG_JSON = `
},
"rater": {
"rals": {
"enabled": false, // enable Rater service: <true|false>
"balancer": "", // register to balancer as worker: <""|internal|x.y.z.y:1234>
"cdrstats": "", // address where to reach the cdrstats service, empty to disable stats functionality: <""|internal|x.y.z.y:1234>
"historys": "", // address where to reach the history service, empty to disable history functionality: <""|internal|x.y.z.y:1234>
"pubsubs": "", // address where to reach the pubusb service, empty to disable pubsub functionality: <""|internal|x.y.z.y:1234>
"users": "", // address where to reach the user service, empty to disable user profile functionality: <""|internal|x.y.z.y:1234>
"aliases": "", // address where to reach the aliases service, empty to disable aliases functionality: <""|internal|x.y.z.y:1234>
"rp_subject_prefix_matching": false // enables prefix matching for the rating profile subject
"balancer": "", // register to balancer as worker: <""|*internal|x.y.z.y:1234>
"cdrstats_conns": [], // address where to reach the cdrstats service, empty to disable stats functionality: <""|*internal|x.y.z.y:1234>
"historys_conns": [], // address where to reach the history service, empty to disable history functionality: <""|*internal|x.y.z.y:1234>
"pubsubs_conns": [], // address where to reach the pubusb service, empty to disable pubsub functionality: <""|*internal|x.y.z.y:1234>
"users_conns": [], // address where to reach the user service, empty to disable user profile functionality: <""|*internal|x.y.z.y:1234>
"aliases_conns": [], // address where to reach the aliases service, empty to disable aliases functionality: <""|*internal|x.y.z.y:1234>
"rp_subject_prefix_matching": false, // enables prefix matching for the rating profile subject
"lcr_subject_prefix_matching": false // enables prefix matching for the lcr subject
},
@@ -112,12 +112,14 @@ const CGRATES_CFG_JSON = `
"enabled": false, // start the CDR Server service: <true|false>
"extra_fields": [], // extra fields to store in CDRs for non-generic CDRs
"store_cdrs": true, // store cdrs in storDb
"rater": "internal", // address where to reach the Rater for cost calculation, empty to disable functionality: <""|internal|x.y.z.y:1234>
"pubsubs": "", // address where to reach the pubusb service, empty to disable pubsub functionality: <""|internal|x.y.z.y:1234>
"users": "", // address where to reach the user service, empty to disable user profile functionality: <""|internal|x.y.z.y:1234>
"aliases": "", // address where to reach the aliases service, empty to disable aliases functionality: <""|internal|x.y.z.y:1234>
"cdrstats": "", // address where to reach the cdrstats service, empty to disable stats functionality<""|internal|x.y.z.y:1234>
"cdr_replication":[], // replicate the raw CDR to a number of servers
"rals_conns": [
{"address": "*internal"} // address where to reach the Rater for cost calculation, empty to disable functionality: <""|*internal|x.y.z.y:1234>
],
"pubsubs_conns": [], // address where to reach the pubusb service, empty to disable pubsub functionality: <""|*internal|x.y.z.y:1234>
"users_conns": [], // address where to reach the user service, empty to disable user profile functionality: <""|*internal|x.y.z.y:1234>
"aliases_conns": [], // address where to reach the aliases service, empty to disable aliases functionality: <""|*internal|x.y.z.y:1234>
"cdrstats_conns": [], // address where to reach the cdrstats service, empty to disable stats functionality<""|*internal|x.y.z.y:1234>
"cdr_replication":[] // replicate the raw CDR to a number of servers
},
@@ -133,7 +135,7 @@ const CGRATES_CFG_JSON = `
"field_separator": ",",
"data_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from KBytes to Bytes)
"sms_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from SMS unit to call duration in some billing systems)
"mms_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from MMS unit to call duration in some billing systems)
"mms_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from MMS unit to call duration in some billing systems)
"generic_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from GENERIC unit to call duration in some billing systems)
"cost_multiply_factor": 1, // multiply cost before export, eg: add VAT
"cost_rounding_decimals": -1, // rounding decimals for Cost values. -1 to disable rounding
@@ -164,11 +166,14 @@ const CGRATES_CFG_JSON = `
},
"cdrc": {
"*default": {
"cdrc": [
{
"id": "*default", // identifier of the CDRC runner
"enabled": false, // enable CDR client functionality
"dry_run": false, // do not send the CDRs to CDRS, just parse them
"cdrs": "internal", // address where to reach CDR server. <internal|x.y.z.y:1234>
"cdrs_conns": [
{"address": "*internal"} // address where to reach CDR server. <*internal|x.y.z.y:1234>
],
"cdr_format": "csv", // CDR file format <csv|freeswitch_csv|fwv|opensips_flatstore>
"field_separator": ",", // separator used in case of csv files
"timezone": "", // timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB>
@@ -198,52 +203,67 @@ const CGRATES_CFG_JSON = `
{"tag": "Usage", "field_id": "Usage", "type": "*composed", "value": "13", "mandatory": true},
],
"trailer_fields": [], // template of the import trailer fields
}
},
},
],
"sm_generic": {
"enabled": false, // starts SessionManager service: <true|false>
"listen_bijson": "127.0.0.1:2014", // address where to listen for bidirectional JSON-RPC requests
"rater": "internal", // address where to reach the Rater <""|internal|127.0.0.1:2013>
"cdrs": "internal", // address where to reach CDR Server <""|internal|x.y.z.y:1234>
"rals_conns": [
{"address": "*internal"} // address where to reach the Rater <""|*internal|127.0.0.1:2013>
],
"cdrs_conns": [
{"address": "*internal"} // address where to reach CDR Server, empty to disable CDR capturing <*internal|x.y.z.y:1234>
],
"debit_interval": "0s", // interval to perform debits on.
"min_call_duration": "0s", // only authorize calls with allowed duration higher than this
"max_call_duration": "3h", // maximum call duration a prepaid call can last
"session_ttl": "0s", // time after a session with no updates is terminated, not defined by default
//"session_ttl_last_used": "", // tweak LastUsed for sessions timing-out, not defined by default
//"session_ttl_usage": "", // tweak Usage for sessions timing-out, not defined by default
},
"sm_freeswitch": {
"enabled": false, // starts SessionManager service: <true|false>
"rater": "internal", // address where to reach the Rater <""|internal|127.0.0.1:2013>
"cdrs": "internal", // address where to reach CDR Server, empty to disable CDR capturing <""|internal|x.y.z.y:1234>
"create_cdr": false, // create CDR out of events and sends it to CDRS component
"extra_fields": [], // extra fields to store in auth/CDRs when creating them
"debit_interval": "10s", // interval to perform debits on.
"min_call_duration": "0s", // only authorize calls with allowed duration higher than this
"max_call_duration": "3h", // maximum call duration a prepaid call can last
"min_dur_low_balance": "5s", // threshold which will trigger low balance warnings for prepaid calls (needs to be lower than debit_interval)
"low_balance_ann_file": "", // file to be played when low balance is reached for prepaid calls
"empty_balance_context": "", // if defined, prepaid calls will be transfered to this context on empty balance
"empty_balance_ann_file": "", // file to be played before disconnecting prepaid calls on empty balance (applies only if no context defined)
"subscribe_park": true, // subscribe via fsock to receive park events
"channel_sync_interval": "5m", // sync channels with freeswitch regularly
"max_wait_connection": "2s", // maximum duration to wait for a connection to be retrieved from the pool
"connections":[ // instantiate connections to multiple FreeSWITCH servers
{"server": "127.0.0.1:8021", "password": "ClueCon", "reconnects": 5}
"rals_conns": [
{"address": "*internal"} // address where to reach the Rater <""|*internal|127.0.0.1:2013>
],
"cdrs_conns": [
{"address": "*internal"} // address where to reach CDR Server, empty to disable CDR capturing <*internal|x.y.z.y:1234>
],
"create_cdr": false, // create CDR out of events and sends them to CDRS component
"extra_fields": [], // extra fields to store in auth/CDRs when creating them
"debit_interval": "10s", // interval to perform debits on.
"min_call_duration": "0s", // only authorize calls with allowed duration higher than this
"max_call_duration": "3h", // maximum call duration a prepaid call can last
"min_dur_low_balance": "5s", // threshold which will trigger low balance warnings for prepaid calls (needs to be lower than debit_interval)
"low_balance_ann_file": "", // file to be played when low balance is reached for prepaid calls
"empty_balance_context": "", // if defined, prepaid calls will be transfered to this context on empty balance
"empty_balance_ann_file": "", // file to be played before disconnecting prepaid calls on empty balance (applies only if no context defined)
"subscribe_park": true, // subscribe via fsock to receive park events
"channel_sync_interval": "5m", // sync channels with freeswitch regularly
"max_wait_connection": "2s", // maximum duration to wait for a connection to be retrieved from the pool
"event_socket_conns":[ // instantiate connections to multiple FreeSWITCH servers
{"address": "127.0.0.1:8021", "password": "ClueCon", "reconnects": 5}
],
},
"sm_kamailio": {
"enabled": false, // starts SessionManager service: <true|false>
"rater": "internal", // address where to reach the Rater <""|internal|127.0.0.1:2013>
"cdrs": "internal", // address where to reach CDR Server, empty to disable CDR capturing <""|internal|x.y.z.y:1234>
"create_cdr": false, // create CDR out of events and sends it to CDRS component
"debit_interval": "10s", // interval to perform debits on.
"min_call_duration": "0s", // only authorize calls with allowed duration higher than this
"max_call_duration": "3h", // maximum call duration a prepaid call can last
"connections":[ // instantiate connections to multiple Kamailio servers
{"evapi_addr": "127.0.0.1:8448", "reconnects": 5}
"enabled": false, // starts SessionManager service: <true|false>
"rals_conns": [
{"address": "*internal"} // address where to reach the Rater <""|*internal|127.0.0.1:2013>
],
"cdrs_conns": [
{"address": "*internal"} // address where to reach CDR Server, empty to disable CDR capturing <*internal|x.y.z.y:1234>
],
"create_cdr": false, // create CDR out of events and sends them to CDRS component
"debit_interval": "10s", // interval to perform debits on.
"min_call_duration": "0s", // only authorize calls with allowed duration higher than this
"max_call_duration": "3h", // maximum call duration a prepaid call can last
"evapi_conns":[ // instantiate connections to multiple Kamailio servers
{"address": "127.0.0.1:8448", "reconnects": 5}
],
},
@@ -251,8 +271,12 @@ const CGRATES_CFG_JSON = `
"sm_opensips": {
"enabled": false, // starts SessionManager service: <true|false>
"listen_udp": "127.0.0.1:2020", // address where to listen for datagram events coming from OpenSIPS
"rater": "internal", // address where to reach the Rater <""|internal|127.0.0.1:2013>
"cdrs": "internal", // address where to reach CDR Server, empty to disable CDR capturing <""|internal|x.y.z.y:1234>
"rals_conns": [
{"address": "*internal"} // address where to reach the Rater <""|*internal|127.0.0.1:2013>
],
"cdrs_conns": [
{"address": "*internal"} // address where to reach CDR Server, empty to disable CDR capturing <*internal|x.y.z.y:1234>
],
"reconnects": 5, // number of reconnects if connection is lost
"create_cdr": false, // create CDR out of events and sends it to CDRS component
"debit_interval": "10s", // interval to perform debits on.
@@ -267,8 +291,10 @@ const CGRATES_CFG_JSON = `
"enabled": false, // enables the diameter agent: <true|false>
"listen": "127.0.0.1:3868", // address where to listen for diameter requests <x.y.z.y:1234>
"dictionaries_dir": "/usr/share/cgrates/diameter/dict/", // path towards directory holding additional dictionaries to load
"sm_generic": "internal", // connection towards SMG component for session management
"pubsubs": "", // address where to reach the pubusb service, empty to disable pubsub functionality: <""|internal|x.y.z.y:1234>
"sm_generic_conns": [
{"address": "*internal"} // connection towards SMG component for session management
],
"pubsubs_conns": [], // address where to reach the pubusb service, empty to disable pubsub functionality: <""|*internal|x.y.z.y:1234>
"create_cdr": true, // create CDR out of CCR terminate and send it to SMG component
"debit_interval": "5m", // interval for CCR updates
"timezone": "", // timezone for timestamps where not specified, empty for general defaults <""|UTC|Local|$IANA_TZ_DB>
@@ -335,7 +361,7 @@ const CGRATES_CFG_JSON = `
"mailer": {
"server": "localhost", // the server to use when sending emails out
"auth_user": "cgrates", // authenticate to email server using this user
"auth_passwd": "CGRateS.org", // authenticate to email server with this password
"auth_password": "CGRateS.org", // authenticate to email server with this password
"from_address": "cgr-mailer@localhost.localdomain" // from address used when sending emails out
},

View File

@@ -33,7 +33,7 @@ const (
DATADB_JSN = "data_db"
STORDB_JSN = "stor_db"
BALANCER_JSN = "balancer"
RATER_JSN = "rater"
RALS_JSN = "rals"
SCHEDULER_JSN = "scheduler"
CDRS_JSN = "cdrs"
MEDIATOR_JSN = "mediator"
@@ -128,12 +128,12 @@ func (self CgrJsonCfg) BalancerJsonCfg() (*BalancerJsonCfg, error) {
return cfg, nil
}
func (self CgrJsonCfg) RaterJsonCfg() (*RaterJsonCfg, error) {
rawCfg, hasKey := self[RATER_JSN]
func (self CgrJsonCfg) RalsJsonCfg() (*RalsJsonCfg, error) {
rawCfg, hasKey := self[RALS_JSN]
if !hasKey {
return nil, nil
}
cfg := new(RaterJsonCfg)
cfg := new(RalsJsonCfg)
if err := json.Unmarshal(*rawCfg, cfg); err != nil {
return nil, err
}
@@ -188,12 +188,12 @@ func (self CgrJsonCfg) CdreJsonCfgs() (map[string]*CdreJsonCfg, error) {
return cfg, nil
}
func (self CgrJsonCfg) CdrcJsonCfg() (map[string]*CdrcJsonCfg, error) {
func (self CgrJsonCfg) CdrcJsonCfg() ([]*CdrcJsonCfg, error) {
rawCfg, hasKey := self[CDRC_JSN]
if !hasKey {
return nil, nil
}
cfg := make(map[string]*CdrcJsonCfg)
cfg := make([]*CdrcJsonCfg, 0)
if err := json.Unmarshal(*rawCfg, &cfg); err != nil {
return nil, err
}

View File

@@ -44,14 +44,13 @@ func TestDfGeneralJsonCfg(t *testing.T) {
Dbdata_encoding: utils.StringPointer("msgpack"),
Tpexport_dir: utils.StringPointer("/var/log/cgrates/tpe"),
Http_failed_dir: utils.StringPointer("/var/log/cgrates/http_failed"),
Default_reqtype: utils.StringPointer(utils.META_RATED),
Default_request_type: utils.StringPointer(utils.META_RATED),
Default_category: utils.StringPointer("call"),
Default_tenant: utils.StringPointer("cgrates.org"),
Default_subject: utils.StringPointer("cgrates"),
Default_timezone: utils.StringPointer("Local"),
Connect_attempts: utils.IntPointer(3),
Reconnects: utils.IntPointer(-1),
Response_cache_ttl: utils.StringPointer("3s"),
Response_cache_ttl: utils.StringPointer("0s"),
Internal_ttl: utils.StringPointer("2m")}
if gCfg, err := dfCgrJsonCfg.GeneralJsonCfg(); err != nil {
t.Error(err)
@@ -74,12 +73,12 @@ func TestDfListenJsonCfg(t *testing.T) {
func TestDfDbJsonCfg(t *testing.T) {
eCfg := &DbJsonCfg{
Db_type: utils.StringPointer("redis"),
Db_host: utils.StringPointer("127.0.0.1"),
Db_port: utils.IntPointer(6379),
Db_name: utils.StringPointer("10"),
Db_user: utils.StringPointer(""),
Db_passwd: utils.StringPointer(""),
Db_type: utils.StringPointer("redis"),
Db_host: utils.StringPointer("127.0.0.1"),
Db_port: utils.IntPointer(6379),
Db_name: utils.StringPointer("10"),
Db_user: utils.StringPointer(""),
Db_password: utils.StringPointer(""),
}
if cfg, err := dfCgrJsonCfg.DbJsonCfg(TPDB_JSN); err != nil {
t.Error(err)
@@ -92,7 +91,7 @@ func TestDfDbJsonCfg(t *testing.T) {
Db_port: utils.IntPointer(6379),
Db_name: utils.StringPointer("11"),
Db_user: utils.StringPointer(""),
Db_passwd: utils.StringPointer(""),
Db_password: utils.StringPointer(""),
Load_history_size: utils.IntPointer(10),
}
if cfg, err := dfCgrJsonCfg.DbJsonCfg(DATADB_JSN); err != nil {
@@ -106,7 +105,7 @@ func TestDfDbJsonCfg(t *testing.T) {
Db_port: utils.IntPointer(3306),
Db_name: utils.StringPointer("cgrates"),
Db_user: utils.StringPointer("cgrates"),
Db_passwd: utils.StringPointer("CGRateS.org"),
Db_password: utils.StringPointer("CGRateS.org"),
Max_open_conns: utils.IntPointer(100),
Max_idle_conns: utils.IntPointer(10),
Cdrs_indexes: utils.StringSlicePointer([]string{}),
@@ -127,10 +126,11 @@ func TestDfBalancerJsonCfg(t *testing.T) {
}
}
func TestDfRaterJsonCfg(t *testing.T) {
eCfg := &RaterJsonCfg{Enabled: utils.BoolPointer(false), Balancer: utils.StringPointer(""), Cdrstats: utils.StringPointer(""),
Historys: utils.StringPointer(""), Pubsubs: utils.StringPointer(""), Users: utils.StringPointer(""), Aliases: utils.StringPointer(""), Rp_subject_prefix_matching: utils.BoolPointer(false)}
if cfg, err := dfCgrJsonCfg.RaterJsonCfg(); err != nil {
func TestDfRalsJsonCfg(t *testing.T) {
eCfg := &RalsJsonCfg{Enabled: utils.BoolPointer(false), Balancer: utils.StringPointer(""), Cdrstats_conns: &[]*HaPoolJsonCfg{},
Historys_conns: &[]*HaPoolJsonCfg{}, Pubsubs_conns: &[]*HaPoolJsonCfg{}, Users_conns: &[]*HaPoolJsonCfg{}, Aliases_conns: &[]*HaPoolJsonCfg{},
Rp_subject_prefix_matching: utils.BoolPointer(false), Lcr_subject_prefix_matching: utils.BoolPointer(false)}
if cfg, err := dfCgrJsonCfg.RalsJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Errorf("Received: %+v", cfg)
@@ -148,14 +148,17 @@ func TestDfSchedulerJsonCfg(t *testing.T) {
func TestDfCdrsJsonCfg(t *testing.T) {
eCfg := &CdrsJsonCfg{
Enabled: utils.BoolPointer(false),
Extra_fields: utils.StringSlicePointer([]string{}),
Store_cdrs: utils.BoolPointer(true),
Rater: utils.StringPointer("internal"),
Pubsubs: utils.StringPointer(""),
Users: utils.StringPointer(""),
Aliases: utils.StringPointer(""),
Cdrstats: utils.StringPointer(""),
Enabled: utils.BoolPointer(false),
Extra_fields: utils.StringSlicePointer([]string{}),
Store_cdrs: utils.BoolPointer(true),
Rals_conns: &[]*HaPoolJsonCfg{
&HaPoolJsonCfg{
Address: utils.StringPointer("*internal"),
}},
Pubsubs_conns: &[]*HaPoolJsonCfg{},
Users_conns: &[]*HaPoolJsonCfg{},
Aliases_conns: &[]*HaPoolJsonCfg{},
Cdrstats_conns: &[]*HaPoolJsonCfg{},
Cdr_replication: &[]*CdrReplicationJsonCfg{},
}
if cfg, err := dfCgrJsonCfg.CdrsJsonCfg(); err != nil {
@@ -299,11 +302,14 @@ func TestDfCdrcJsonCfg(t *testing.T) {
&CdrFieldJsonCfg{Tag: utils.StringPointer("Usage"), Field_id: utils.StringPointer(utils.USAGE), Type: utils.StringPointer(utils.META_COMPOSED),
Value: utils.StringPointer("13"), Mandatory: utils.BoolPointer(true)},
}
eCfg := map[string]*CdrcJsonCfg{
"*default": &CdrcJsonCfg{
Enabled: utils.BoolPointer(false),
Dry_run: utils.BoolPointer(false),
Cdrs: utils.StringPointer("internal"),
eCfg := []*CdrcJsonCfg{
&CdrcJsonCfg{
Id: utils.StringPointer(utils.META_DEFAULT),
Enabled: utils.BoolPointer(false),
Dry_run: utils.BoolPointer(false),
Cdrs_conns: &[]*HaPoolJsonCfg{&HaPoolJsonCfg{
Address: utils.StringPointer(utils.MetaInternal),
}},
Cdr_format: utils.StringPointer("csv"),
Field_separator: utils.StringPointer(","),
Timezone: utils.StringPointer(""),
@@ -325,19 +331,26 @@ func TestDfCdrcJsonCfg(t *testing.T) {
if cfg, err := dfCgrJsonCfg.CdrcJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Error("Received: ", cfg["*default"])
t.Errorf("Expecting: \n%s\n, received: \n%s\n: ", utils.ToIJSON(eCfg), utils.ToIJSON(cfg))
}
}
func TestSmGenericJsonCfg(t *testing.T) {
eCfg := &SmGenericJsonCfg{
Enabled: utils.BoolPointer(false),
Listen_bijson: utils.StringPointer("127.0.0.1:2014"),
Rater: utils.StringPointer("internal"),
Cdrs: utils.StringPointer("internal"),
Enabled: utils.BoolPointer(false),
Listen_bijson: utils.StringPointer("127.0.0.1:2014"),
Rals_conns: &[]*HaPoolJsonCfg{
&HaPoolJsonCfg{
Address: utils.StringPointer(utils.MetaInternal),
}},
Cdrs_conns: &[]*HaPoolJsonCfg{
&HaPoolJsonCfg{
Address: utils.StringPointer(utils.MetaInternal),
}},
Debit_interval: utils.StringPointer("0s"),
Min_call_duration: utils.StringPointer("0s"),
Max_call_duration: utils.StringPointer("3h"),
Session_ttl: utils.StringPointer("0s"),
}
if cfg, err := dfCgrJsonCfg.SmGenericJsonCfg(); err != nil {
t.Error(err)
@@ -348,9 +361,15 @@ func TestSmGenericJsonCfg(t *testing.T) {
func TestSmFsJsonCfg(t *testing.T) {
eCfg := &SmFsJsonCfg{
Enabled: utils.BoolPointer(false),
Rater: utils.StringPointer("internal"),
Cdrs: utils.StringPointer("internal"),
Enabled: utils.BoolPointer(false),
Rals_conns: &[]*HaPoolJsonCfg{
&HaPoolJsonCfg{
Address: utils.StringPointer(utils.MetaInternal),
}},
Cdrs_conns: &[]*HaPoolJsonCfg{
&HaPoolJsonCfg{
Address: utils.StringPointer(utils.MetaInternal),
}},
Create_cdr: utils.BoolPointer(false),
Extra_fields: utils.StringSlicePointer([]string{}),
Debit_interval: utils.StringPointer("10s"),
@@ -363,9 +382,9 @@ func TestSmFsJsonCfg(t *testing.T) {
Subscribe_park: utils.BoolPointer(true),
Channel_sync_interval: utils.StringPointer("5m"),
Max_wait_connection: utils.StringPointer("2s"),
Connections: &[]*FsConnJsonCfg{
Event_socket_conns: &[]*FsConnJsonCfg{
&FsConnJsonCfg{
Server: utils.StringPointer("127.0.0.1:8021"),
Address: utils.StringPointer("127.0.0.1:8021"),
Password: utils.StringPointer("ClueCon"),
Reconnects: utils.IntPointer(5),
}},
@@ -379,16 +398,22 @@ func TestSmFsJsonCfg(t *testing.T) {
func TestSmKamJsonCfg(t *testing.T) {
eCfg := &SmKamJsonCfg{
Enabled: utils.BoolPointer(false),
Rater: utils.StringPointer("internal"),
Cdrs: utils.StringPointer("internal"),
Enabled: utils.BoolPointer(false),
Rals_conns: &[]*HaPoolJsonCfg{
&HaPoolJsonCfg{
Address: utils.StringPointer(utils.MetaInternal),
}},
Cdrs_conns: &[]*HaPoolJsonCfg{
&HaPoolJsonCfg{
Address: utils.StringPointer(utils.MetaInternal),
}},
Create_cdr: utils.BoolPointer(false),
Debit_interval: utils.StringPointer("10s"),
Min_call_duration: utils.StringPointer("0s"),
Max_call_duration: utils.StringPointer("3h"),
Connections: &[]*KamConnJsonCfg{
Evapi_conns: &[]*KamConnJsonCfg{
&KamConnJsonCfg{
Evapi_addr: utils.StringPointer("127.0.0.1:8448"),
Address: utils.StringPointer("127.0.0.1:8448"),
Reconnects: utils.IntPointer(5),
},
},
@@ -402,10 +427,16 @@ func TestSmKamJsonCfg(t *testing.T) {
func TestSmOsipsJsonCfg(t *testing.T) {
eCfg := &SmOsipsJsonCfg{
Enabled: utils.BoolPointer(false),
Listen_udp: utils.StringPointer("127.0.0.1:2020"),
Rater: utils.StringPointer("internal"),
Cdrs: utils.StringPointer("internal"),
Enabled: utils.BoolPointer(false),
Listen_udp: utils.StringPointer("127.0.0.1:2020"),
Rals_conns: &[]*HaPoolJsonCfg{
&HaPoolJsonCfg{
Address: utils.StringPointer(utils.MetaInternal),
}},
Cdrs_conns: &[]*HaPoolJsonCfg{
&HaPoolJsonCfg{
Address: utils.StringPointer(utils.MetaInternal),
}},
Create_cdr: utils.BoolPointer(false),
Debit_interval: utils.StringPointer("10s"),
Min_call_duration: utils.StringPointer("0s"),
@@ -425,16 +456,19 @@ func TestDiameterAgentJsonCfg(t *testing.T) {
Enabled: utils.BoolPointer(false),
Listen: utils.StringPointer("127.0.0.1:3868"),
Dictionaries_dir: utils.StringPointer("/usr/share/cgrates/diameter/dict/"),
Sm_generic: utils.StringPointer("internal"),
Pubsubs: utils.StringPointer(""),
Create_cdr: utils.BoolPointer(true),
Debit_interval: utils.StringPointer("5m"),
Timezone: utils.StringPointer(""),
Dialect: utils.StringPointer("huawei"),
Origin_host: utils.StringPointer("CGR-DA"),
Origin_realm: utils.StringPointer("cgrates.org"),
Vendor_id: utils.IntPointer(0),
Product_name: utils.StringPointer("CGRateS"),
Sm_generic_conns: &[]*HaPoolJsonCfg{
&HaPoolJsonCfg{
Address: utils.StringPointer(utils.MetaInternal),
}},
Pubsubs_conns: &[]*HaPoolJsonCfg{},
Create_cdr: utils.BoolPointer(true),
Debit_interval: utils.StringPointer("5m"),
Timezone: utils.StringPointer(""),
Dialect: utils.StringPointer("huawei"),
Origin_host: utils.StringPointer("CGR-DA"),
Origin_realm: utils.StringPointer("cgrates.org"),
Vendor_id: utils.IntPointer(0),
Product_name: utils.StringPointer("CGRateS"),
Request_processors: &[]*DARequestProcessorJsnCfg{
&DARequestProcessorJsnCfg{
Id: utils.StringPointer("*default"),
@@ -536,10 +570,10 @@ func TestDfUserServJsonCfg(t *testing.T) {
func TestDfMailerJsonCfg(t *testing.T) {
eCfg := &MailerJsonCfg{
Server: utils.StringPointer("localhost"),
Auth_user: utils.StringPointer("cgrates"),
Auth_passwd: utils.StringPointer("CGRateS.org"),
From_address: utils.StringPointer("cgr-mailer@localhost.localdomain"),
Server: utils.StringPointer("localhost"),
Auth_user: utils.StringPointer("cgrates"),
Auth_password: utils.StringPointer("CGRateS.org"),
From_address: utils.StringPointer("cgr-mailer@localhost.localdomain"),
}
if cfg, err := dfCgrJsonCfg.MailerJsonCfg(); err != nil {
t.Error(err)
@@ -589,25 +623,27 @@ func TestNewCgrJsonCfgFromFile(t *testing.T) {
if err != nil {
t.Error(err)
}
eCfg := &GeneralJsonCfg{Default_reqtype: utils.StringPointer(utils.META_PSEUDOPREPAID)}
eCfg := &GeneralJsonCfg{Default_request_type: utils.StringPointer(utils.META_PSEUDOPREPAID)}
if gCfg, err := cgrJsonCfg.GeneralJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, gCfg) {
t.Error("Received: ", gCfg)
t.Errorf("Expecting: %+v, received: ", eCfg, gCfg)
}
cdrFields := []*CdrFieldJsonCfg{
&CdrFieldJsonCfg{Field_id: utils.StringPointer(utils.TOR), Value: utils.StringPointer("~7:s/^(voice|data|sms|mms|generic)$/*$1/")},
&CdrFieldJsonCfg{Field_id: utils.StringPointer(utils.ANSWER_TIME), Value: utils.StringPointer("1")},
&CdrFieldJsonCfg{Field_id: utils.StringPointer(utils.USAGE), Value: utils.StringPointer(`~9:s/^(\d+)$/${1}s/`)},
}
eCfgCdrc := map[string]*CdrcJsonCfg{
"CDRC-CSV1": &CdrcJsonCfg{
eCfgCdrc := []*CdrcJsonCfg{
&CdrcJsonCfg{
Id: utils.StringPointer("CDRC-CSV1"),
Enabled: utils.BoolPointer(true),
Cdr_in_dir: utils.StringPointer("/tmp/cgrates/cdrc1/in"),
Cdr_out_dir: utils.StringPointer("/tmp/cgrates/cdrc1/out"),
Cdr_source_id: utils.StringPointer("csv1"),
},
"CDRC-CSV2": &CdrcJsonCfg{
&CdrcJsonCfg{
Id: utils.StringPointer("CDRC-CSV2"),
Enabled: utils.BoolPointer(true),
Data_usage_multiply_factor: utils.Float64Pointer(0.000976563),
Run_delay: utils.IntPointer(1),
@@ -620,18 +656,18 @@ func TestNewCgrJsonCfgFromFile(t *testing.T) {
if cfg, err := cgrJsonCfg.CdrcJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfgCdrc, cfg) {
t.Error("Received: ", utils.ToIJSON(cfg["CDRC-CSV2"]))
t.Errorf("Expecting:\n %+v\n received:\n %+v\n", utils.ToIJSON(eCfgCdrc), utils.ToIJSON(cfg))
}
eCfgSmFs := &SmFsJsonCfg{
Enabled: utils.BoolPointer(true),
Connections: &[]*FsConnJsonCfg{
Event_socket_conns: &[]*FsConnJsonCfg{
&FsConnJsonCfg{
Server: utils.StringPointer("1.2.3.4:8021"),
Address: utils.StringPointer("1.2.3.4:8021"),
Password: utils.StringPointer("ClueCon"),
Reconnects: utils.IntPointer(5),
},
&FsConnJsonCfg{
Server: utils.StringPointer("2.3.4.5:8021"),
Address: utils.StringPointer("2.3.4.5:8021"),
Password: utils.StringPointer("ClueCon"),
Reconnects: utils.IntPointer(5),
},

View File

@@ -39,18 +39,18 @@ func TestLoadCgrCfgWithDefaults(t *testing.T) {
{
"sm_freeswitch": {
"enabled": true, // starts SessionManager service: <true|false>
"connections":[ // instantiate connections to multiple FreeSWITCH servers
{"server": "1.2.3.4:8021", "password": "ClueCon", "reconnects": 3},
{"server": "1.2.3.5:8021", "password": "ClueCon", "reconnects": 5}
"event_socket_conns":[ // instantiate connections to multiple FreeSWITCH servers
{"address": "1.2.3.4:8021", "password": "ClueCon", "reconnects": 3},
{"address": "1.2.3.5:8021", "password": "ClueCon", "reconnects": 5}
],
},
}`
eCgrCfg, _ := NewDefaultCGRConfig()
eCgrCfg.SmFsConfig.Enabled = true
eCgrCfg.SmFsConfig.Connections = []*FsConnConfig{
&FsConnConfig{Server: "1.2.3.4:8021", Password: "ClueCon", Reconnects: 3},
&FsConnConfig{Server: "1.2.3.5:8021", Password: "ClueCon", Reconnects: 5},
eCgrCfg.SmFsConfig.EventSocketConns = []*FsConnConfig{
&FsConnConfig{Address: "1.2.3.4:8021", Password: "ClueCon", Reconnects: 3},
&FsConnConfig{Address: "1.2.3.5:8021", Password: "ClueCon", Reconnects: 5},
}
if cgrCfg, err := NewCGRConfigFromJsonStringWithDefaults(JSN_CFG); err != nil {
t.Error(err)

View File

@@ -32,12 +32,13 @@ func TestLoadCdrcConfigMultipleFiles(t *testing.T) {
t.Error(err)
}
eCgrCfg, _ := NewDefaultCGRConfig()
eCgrCfg.CdrcProfiles = make(map[string]map[string]*CdrcConfig)
eCgrCfg.CdrcProfiles = make(map[string][]*CdrcConfig)
// Default instance first
eCgrCfg.CdrcProfiles["/var/log/cgrates/cdrc/in"] = map[string]*CdrcConfig{
"*default": &CdrcConfig{
eCgrCfg.CdrcProfiles["/var/log/cgrates/cdrc/in"] = []*CdrcConfig{
&CdrcConfig{
ID: utils.META_DEFAULT,
Enabled: false,
Cdrs: "internal",
CdrsConns: []*HaPoolConfig{&HaPoolConfig{Address: utils.MetaInternal}},
CdrFormat: "csv",
FieldSeparator: ',',
DataUsageMultiplyFactor: 1024,
@@ -79,10 +80,11 @@ func TestLoadCdrcConfigMultipleFiles(t *testing.T) {
TrailerFields: make([]*CfgCdrField, 0),
},
}
eCgrCfg.CdrcProfiles["/tmp/cgrates/cdrc1/in"] = map[string]*CdrcConfig{
"CDRC-CSV1": &CdrcConfig{
eCgrCfg.CdrcProfiles["/tmp/cgrates/cdrc1/in"] = []*CdrcConfig{
&CdrcConfig{
ID: "CDRC-CSV1",
Enabled: true,
Cdrs: "internal",
CdrsConns: []*HaPoolConfig{&HaPoolConfig{Address: utils.MetaInternal}},
CdrFormat: "csv",
FieldSeparator: ',',
DataUsageMultiplyFactor: 1024,
@@ -122,14 +124,15 @@ func TestLoadCdrcConfigMultipleFiles(t *testing.T) {
TrailerFields: make([]*CfgCdrField, 0),
},
}
eCgrCfg.CdrcProfiles["/tmp/cgrates/cdrc2/in"] = map[string]*CdrcConfig{
"CDRC-CSV2": &CdrcConfig{
eCgrCfg.CdrcProfiles["/tmp/cgrates/cdrc2/in"] = []*CdrcConfig{
&CdrcConfig{
ID: "CDRC-CSV2",
Enabled: true,
Cdrs: "internal",
CdrsConns: []*HaPoolConfig{&HaPoolConfig{Address: utils.MetaInternal}},
CdrFormat: "csv",
FieldSeparator: ',',
DataUsageMultiplyFactor: 0.000976563,
RunDelay: 0,
RunDelay: 1000000000,
MaxOpenFiles: 1024,
CdrInDir: "/tmp/cgrates/cdrc2/in",
CdrOutDir: "/tmp/cgrates/cdrc2/out",
@@ -137,18 +140,21 @@ func TestLoadCdrcConfigMultipleFiles(t *testing.T) {
CdrFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP),
HeaderFields: make([]*CfgCdrField, 0),
ContentFields: []*CfgCdrField{
&CfgCdrField{Tag: "", Type: "", FieldId: utils.TOR, Value: utils.ParseRSRFieldsMustCompile("~7:s/^(voice|data|sms|generic)$/*$1/", utils.INFIELD_SEP),
&CfgCdrField{FieldId: utils.TOR, Value: utils.ParseRSRFieldsMustCompile("~7:s/^(voice|data|sms|mms|generic)$/*$1/", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: false},
&CfgCdrField{Tag: "", Type: "", FieldId: utils.ANSWER_TIME, Value: utils.ParseRSRFieldsMustCompile("2", utils.INFIELD_SEP),
&CfgCdrField{Tag: "", Type: "", FieldId: utils.ANSWER_TIME, Value: utils.ParseRSRFieldsMustCompile("1", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: false},
&CfgCdrField{FieldId: utils.USAGE, Value: utils.ParseRSRFieldsMustCompile("~9:s/^(\\d+)$/${1}s/", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: false},
},
TrailerFields: make([]*CfgCdrField, 0),
},
}
eCgrCfg.CdrcProfiles["/tmp/cgrates/cdrc3/in"] = map[string]*CdrcConfig{
"CDRC-CSV3": &CdrcConfig{
eCgrCfg.CdrcProfiles["/tmp/cgrates/cdrc3/in"] = []*CdrcConfig{
&CdrcConfig{
ID: "CDRC-CSV3",
Enabled: true,
Cdrs: "internal",
CdrsConns: []*HaPoolConfig{&HaPoolConfig{Address: utils.MetaInternal}},
CdrFormat: "csv",
FieldSeparator: ',',
DataUsageMultiplyFactor: 1024,
@@ -189,6 +195,6 @@ func TestLoadCdrcConfigMultipleFiles(t *testing.T) {
},
}
if !reflect.DeepEqual(eCgrCfg.CdrcProfiles, cgrCfg.CdrcProfiles) {
t.Errorf("Expected: %+v, received: %+v", eCgrCfg.CdrcProfiles, cgrCfg.CdrcProfiles)
t.Errorf("Expected: \n%s\n, received: \n%s\n", utils.ToJSON(eCgrCfg.CdrcProfiles), utils.ToJSON(cgrCfg.CdrcProfiles))
}
}

View File

@@ -28,8 +28,8 @@ type DiameterAgentCfg struct {
Enabled bool // enables the diameter agent: <true|false>
Listen string // address where to listen for diameter requests <x.y.z.y:1234>
DictionariesDir string
SMGeneric string // connection towards SMG component
PubSubS string // connection towards pubsubs
SMGenericConns []*HaPoolConfig // connections towards SMG component
PubSubConns []*HaPoolConfig // connection towards pubsubs
CreateCDR bool
DebitInterval time.Duration
Timezone string // timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB>
@@ -54,11 +54,19 @@ func (self *DiameterAgentCfg) loadFromJsonCfg(jsnCfg *DiameterAgentJsonCfg) erro
if jsnCfg.Dictionaries_dir != nil {
self.DictionariesDir = *jsnCfg.Dictionaries_dir
}
if jsnCfg.Sm_generic != nil {
self.SMGeneric = *jsnCfg.Sm_generic
if jsnCfg.Sm_generic_conns != nil {
self.SMGenericConns = make([]*HaPoolConfig, len(*jsnCfg.Sm_generic_conns))
for idx, jsnHaCfg := range *jsnCfg.Sm_generic_conns {
self.SMGenericConns[idx] = NewDfltHaPoolConfig()
self.SMGenericConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnCfg.Pubsubs != nil {
self.PubSubS = *jsnCfg.Pubsubs
if jsnCfg.Pubsubs_conns != nil {
self.PubSubConns = make([]*HaPoolConfig, len(*jsnCfg.Pubsubs_conns))
for idx, jsnHaCfg := range *jsnCfg.Pubsubs_conns {
self.PubSubConns[idx] = NewDfltHaPoolConfig()
self.PubSubConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnCfg.Create_cdr != nil {
self.CreateCDR = *jsnCfg.Create_cdr

View File

@@ -26,12 +26,12 @@ import (
type CdrReplicationCfg struct {
Transport string
Server string
Address string
Synchronous bool
Attempts int // Number of attempts if not success
CdrFilter utils.RSRFields // Only replicate if the filters here are matching
}
func (rplCfg CdrReplicationCfg) FallbackFileName() string {
return fmt.Sprintf("cdr_%s_%s_%s.form", rplCfg.Transport, url.QueryEscape(rplCfg.Server), utils.GenUUID())
return fmt.Sprintf("cdr_%s_%s_%s.form", rplCfg.Transport, url.QueryEscape(rplCfg.Address), utils.GenUUID())
}

View File

@@ -25,10 +25,9 @@ type GeneralJsonCfg struct {
Dbdata_encoding *string
Tpexport_dir *string
Http_failed_dir *string
Default_reqtype *string
Default_request_type *string
Default_category *string
Default_tenant *string
Default_subject *string
Default_timezone *string
Connect_attempts *int
Reconnects *int
@@ -50,7 +49,7 @@ type DbJsonCfg struct {
Db_port *int
Db_name *string
Db_user *string
Db_passwd *string
Db_password *string
Max_open_conns *int // Used only in case of storDb
Max_idle_conns *int
Load_history_size *int // Used in case of dataDb to limit the length of the loads history
@@ -63,15 +62,16 @@ type BalancerJsonCfg struct {
}
// Rater config section
type RaterJsonCfg struct {
Enabled *bool
Balancer *string
Cdrstats *string
Historys *string
Pubsubs *string
Aliases *string
Users *string
Rp_subject_prefix_matching *bool
type RalsJsonCfg struct {
Enabled *bool
Balancer *string
Cdrstats_conns *[]*HaPoolJsonCfg
Historys_conns *[]*HaPoolJsonCfg
Pubsubs_conns *[]*HaPoolJsonCfg
Aliases_conns *[]*HaPoolJsonCfg
Users_conns *[]*HaPoolJsonCfg
Rp_subject_prefix_matching *bool
Lcr_subject_prefix_matching *bool
}
// Scheduler config section
@@ -84,17 +84,17 @@ type CdrsJsonCfg struct {
Enabled *bool
Extra_fields *[]string
Store_cdrs *bool
Rater *string
Pubsubs *string
Users *string
Aliases *string
Cdrstats *string
Rals_conns *[]*HaPoolJsonCfg
Pubsubs_conns *[]*HaPoolJsonCfg
Users_conns *[]*HaPoolJsonCfg
Aliases_conns *[]*HaPoolJsonCfg
Cdrstats_conns *[]*HaPoolJsonCfg
Cdr_replication *[]*CdrReplicationJsonCfg
}
type CdrReplicationJsonCfg struct {
Transport *string
Server *string
Address *string
Synchronous *bool
Attempts *int
Cdr_filter *string
@@ -143,9 +143,10 @@ type CdreJsonCfg struct {
// Cdrc config section
type CdrcJsonCfg struct {
Id *string
Enabled *bool
Dry_run *bool
Cdrs *string
Cdrs_conns *[]*HaPoolJsonCfg
Cdr_format *string
Field_separator *string
Timezone *string
@@ -166,20 +167,23 @@ type CdrcJsonCfg struct {
// SM-Generic config section
type SmGenericJsonCfg struct {
Enabled *bool
Listen_bijson *string
Rater *string
Cdrs *string
Debit_interval *string
Min_call_duration *string
Max_call_duration *string
Enabled *bool
Listen_bijson *string
Rals_conns *[]*HaPoolJsonCfg
Cdrs_conns *[]*HaPoolJsonCfg
Debit_interval *string
Min_call_duration *string
Max_call_duration *string
Session_ttl *string
Session_ttl_last_used *string
Session_ttl_usage *string
}
// SM-FreeSWITCH config section
type SmFsJsonCfg struct {
Enabled *bool
Rater *string
Cdrs *string
Rals_conns *[]*HaPoolJsonCfg
Cdrs_conns *[]*HaPoolJsonCfg
Create_cdr *bool
Extra_fields *[]string
Debit_interval *string
@@ -192,12 +196,17 @@ type SmFsJsonCfg struct {
Subscribe_park *bool
Channel_sync_interval *string
Max_wait_connection *string
Connections *[]*FsConnJsonCfg
Event_socket_conns *[]*FsConnJsonCfg
}
// Represents one connection instance towards a rater/cdrs server
type HaPoolJsonCfg struct {
Address *string
}
// Represents one connection instance towards FreeSWITCH
type FsConnJsonCfg struct {
Server *string
Address *string
Password *string
Reconnects *int
}
@@ -205,18 +214,18 @@ type FsConnJsonCfg struct {
// SM-Kamailio config section
type SmKamJsonCfg struct {
Enabled *bool
Rater *string
Cdrs *string
Rals_conns *[]*HaPoolJsonCfg
Cdrs_conns *[]*HaPoolJsonCfg
Create_cdr *bool
Debit_interval *string
Min_call_duration *string
Max_call_duration *string
Connections *[]*KamConnJsonCfg
Evapi_conns *[]*KamConnJsonCfg
}
// Represents one connection instance towards Kamailio
type KamConnJsonCfg struct {
Evapi_addr *string
Address *string
Reconnects *int
}
@@ -224,8 +233,8 @@ type KamConnJsonCfg struct {
type SmOsipsJsonCfg struct {
Enabled *bool
Listen_udp *string
Rater *string
Cdrs *string
Rals_conns *[]*HaPoolJsonCfg
Cdrs_conns *[]*HaPoolJsonCfg
Create_cdr *bool
Debit_interval *string
Min_call_duration *string
@@ -242,11 +251,11 @@ type OsipsConnJsonCfg struct {
// DiameterAgent configuration
type DiameterAgentJsonCfg struct {
Enabled *bool // enables the diameter agent: <true|false>
Listen *string // address where to listen for diameter requests <x.y.z.y:1234>
Dictionaries_dir *string // path towards additional dictionaries
Sm_generic *string // connection towards generic SM
Pubsubs *string // connection towards pubsubs
Enabled *bool // enables the diameter agent: <true|false>
Listen *string // address where to listen for diameter requests <x.y.z.y:1234>
Dictionaries_dir *string // path towards additional dictionaries
Sm_generic_conns *[]*HaPoolJsonCfg // Connections towards generic SM
Pubsubs_conns *[]*HaPoolJsonCfg // connection towards pubsubs
Create_cdr *bool
Debit_interval *string
Timezone *string // timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB>
@@ -296,10 +305,10 @@ type UserServJsonCfg struct {
// Mailer config section
type MailerJsonCfg struct {
Server *string
Auth_user *string
Auth_passwd *string
From_address *string
Server *string
Auth_user *string
Auth_password *string
From_address *string
}
// SureTax config section

View File

@@ -35,8 +35,17 @@ func NewDfltHaPoolConfig() *HaPoolConfig {
// One connection to Rater
type HaPoolConfig struct {
Server string
Timeout time.Duration
Address string
}
func (self *HaPoolConfig) loadFromJsonCfg(jsnCfg *HaPoolJsonCfg) error {
if jsnCfg == nil {
return nil
}
if jsnCfg.Address != nil {
self.Address = *jsnCfg.Address
}
return nil
}
// Returns the first cached default value for a SM-FreeSWITCH connection
@@ -50,7 +59,7 @@ func NewDfltFsConnConfig() *FsConnConfig {
// One connection to FreeSWITCH server
type FsConnConfig struct {
Server string
Address string
Password string
Reconnects int
}
@@ -59,8 +68,8 @@ func (self *FsConnConfig) loadFromJsonCfg(jsnCfg *FsConnJsonCfg) error {
if jsnCfg == nil {
return nil
}
if jsnCfg.Server != nil {
self.Server = *jsnCfg.Server
if jsnCfg.Address != nil {
self.Address = *jsnCfg.Address
}
if jsnCfg.Password != nil {
self.Password = *jsnCfg.Password
@@ -72,13 +81,16 @@ func (self *FsConnConfig) loadFromJsonCfg(jsnCfg *FsConnJsonCfg) error {
}
type SmGenericConfig struct {
Enabled bool
ListenBijson string
HaRater []*HaPoolConfig
HaCdrs []*HaPoolConfig
DebitInterval time.Duration
MinCallDuration time.Duration
MaxCallDuration time.Duration
Enabled bool
ListenBijson string
RALsConns []*HaPoolConfig
CDRsConns []*HaPoolConfig
DebitInterval time.Duration
MinCallDuration time.Duration
MaxCallDuration time.Duration
SessionTTL time.Duration
SessionTTLLastUsed *time.Duration
SessionTTLUsage *time.Duration
}
func (self *SmGenericConfig) loadFromJsonCfg(jsnCfg *SmGenericJsonCfg) error {
@@ -92,11 +104,19 @@ func (self *SmGenericConfig) loadFromJsonCfg(jsnCfg *SmGenericJsonCfg) error {
if jsnCfg.Listen_bijson != nil {
self.ListenBijson = *jsnCfg.Listen_bijson
}
if jsnCfg.Rater != nil {
self.HaRater = []*HaPoolConfig{&HaPoolConfig{Server: *jsnCfg.Rater, Timeout: time.Duration(1) * time.Second}}
if jsnCfg.Rals_conns != nil {
self.RALsConns = make([]*HaPoolConfig, len(*jsnCfg.Rals_conns))
for idx, jsnHaCfg := range *jsnCfg.Rals_conns {
self.RALsConns[idx] = NewDfltHaPoolConfig()
self.RALsConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnCfg.Cdrs != nil {
self.HaCdrs = []*HaPoolConfig{&HaPoolConfig{Server: *jsnCfg.Cdrs, Timeout: time.Duration(1) * time.Second}}
if jsnCfg.Cdrs_conns != nil {
self.CDRsConns = make([]*HaPoolConfig, len(*jsnCfg.Cdrs_conns))
for idx, jsnHaCfg := range *jsnCfg.Cdrs_conns {
self.CDRsConns[idx] = NewDfltHaPoolConfig()
self.CDRsConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnCfg.Debit_interval != nil {
if self.DebitInterval, err = utils.ParseDurationWithSecs(*jsnCfg.Debit_interval); err != nil {
@@ -113,13 +133,32 @@ func (self *SmGenericConfig) loadFromJsonCfg(jsnCfg *SmGenericJsonCfg) error {
return err
}
}
if jsnCfg.Session_ttl != nil {
if self.SessionTTL, err = utils.ParseDurationWithSecs(*jsnCfg.Session_ttl); err != nil {
return err
}
}
if jsnCfg.Session_ttl_last_used != nil {
if sessionTTLLastUsed, err := utils.ParseDurationWithSecs(*jsnCfg.Session_ttl_last_used); err != nil {
return err
} else {
self.SessionTTLLastUsed = &sessionTTLLastUsed
}
}
if jsnCfg.Session_ttl_usage != nil {
if sessionTTLUsage, err := utils.ParseDurationWithSecs(*jsnCfg.Session_ttl_usage); err != nil {
return err
} else {
self.SessionTTLUsage = &sessionTTLUsage
}
}
return nil
}
type SmFsConfig struct {
Enabled bool
HaRater []*HaPoolConfig
HaCdrs []*HaPoolConfig
RALsConns []*HaPoolConfig
CDRsConns []*HaPoolConfig
CreateCdr bool
ExtraFields []*utils.RSRField
DebitInterval time.Duration
@@ -132,7 +171,7 @@ type SmFsConfig struct {
SubscribePark bool
ChannelSyncInterval time.Duration
MaxWaitConnection time.Duration
Connections []*FsConnConfig
EventSocketConns []*FsConnConfig
}
func (self *SmFsConfig) loadFromJsonCfg(jsnCfg *SmFsJsonCfg) error {
@@ -143,11 +182,19 @@ func (self *SmFsConfig) loadFromJsonCfg(jsnCfg *SmFsJsonCfg) error {
if jsnCfg.Enabled != nil {
self.Enabled = *jsnCfg.Enabled
}
if jsnCfg.Rater != nil {
self.HaRater = []*HaPoolConfig{&HaPoolConfig{Server: *jsnCfg.Rater, Timeout: time.Duration(1) * time.Second}}
if jsnCfg.Rals_conns != nil {
self.RALsConns = make([]*HaPoolConfig, len(*jsnCfg.Rals_conns))
for idx, jsnHaCfg := range *jsnCfg.Rals_conns {
self.RALsConns[idx] = NewDfltHaPoolConfig()
self.RALsConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnCfg.Cdrs != nil {
self.HaCdrs = []*HaPoolConfig{&HaPoolConfig{Server: *jsnCfg.Cdrs, Timeout: time.Duration(1) * time.Second}}
if jsnCfg.Cdrs_conns != nil {
self.CDRsConns = make([]*HaPoolConfig, len(*jsnCfg.Cdrs_conns))
for idx, jsnHaCfg := range *jsnCfg.Cdrs_conns {
self.CDRsConns[idx] = NewDfltHaPoolConfig()
self.CDRsConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnCfg.Create_cdr != nil {
self.CreateCdr = *jsnCfg.Create_cdr
@@ -199,11 +246,11 @@ func (self *SmFsConfig) loadFromJsonCfg(jsnCfg *SmFsJsonCfg) error {
return err
}
}
if jsnCfg.Connections != nil {
self.Connections = make([]*FsConnConfig, len(*jsnCfg.Connections))
for idx, jsnConnCfg := range *jsnCfg.Connections {
self.Connections[idx] = NewDfltFsConnConfig()
self.Connections[idx].loadFromJsonCfg(jsnConnCfg)
if jsnCfg.Event_socket_conns != nil {
self.EventSocketConns = make([]*FsConnConfig, len(*jsnCfg.Event_socket_conns))
for idx, jsnConnCfg := range *jsnCfg.Event_socket_conns {
self.EventSocketConns[idx] = NewDfltFsConnConfig()
self.EventSocketConns[idx].loadFromJsonCfg(jsnConnCfg)
}
}
return nil
@@ -220,7 +267,7 @@ func NewDfltKamConnConfig() *KamConnConfig {
// Represents one connection instance towards Kamailio
type KamConnConfig struct {
EvapiAddr string
Address string
Reconnects int
}
@@ -228,8 +275,8 @@ func (self *KamConnConfig) loadFromJsonCfg(jsnCfg *KamConnJsonCfg) error {
if jsnCfg == nil {
return nil
}
if jsnCfg.Evapi_addr != nil {
self.EvapiAddr = *jsnCfg.Evapi_addr
if jsnCfg.Address != nil {
self.Address = *jsnCfg.Address
}
if jsnCfg.Reconnects != nil {
self.Reconnects = *jsnCfg.Reconnects
@@ -240,13 +287,13 @@ func (self *KamConnConfig) loadFromJsonCfg(jsnCfg *KamConnJsonCfg) error {
// SM-Kamailio config section
type SmKamConfig struct {
Enabled bool
HaRater []*HaPoolConfig
HaCdrs []*HaPoolConfig
RALsConns []*HaPoolConfig
CDRsConns []*HaPoolConfig
CreateCdr bool
DebitInterval time.Duration
MinCallDuration time.Duration
MaxCallDuration time.Duration
Connections []*KamConnConfig
EvapiConns []*KamConnConfig
}
func (self *SmKamConfig) loadFromJsonCfg(jsnCfg *SmKamJsonCfg) error {
@@ -257,11 +304,19 @@ func (self *SmKamConfig) loadFromJsonCfg(jsnCfg *SmKamJsonCfg) error {
if jsnCfg.Enabled != nil {
self.Enabled = *jsnCfg.Enabled
}
if jsnCfg.Rater != nil {
self.HaRater = []*HaPoolConfig{&HaPoolConfig{Server: *jsnCfg.Rater, Timeout: time.Duration(1) * time.Second}}
if jsnCfg.Rals_conns != nil {
self.RALsConns = make([]*HaPoolConfig, len(*jsnCfg.Rals_conns))
for idx, jsnHaCfg := range *jsnCfg.Rals_conns {
self.RALsConns[idx] = NewDfltHaPoolConfig()
self.RALsConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnCfg.Cdrs != nil {
self.HaCdrs = []*HaPoolConfig{&HaPoolConfig{Server: *jsnCfg.Cdrs, Timeout: time.Duration(1) * time.Second}}
if jsnCfg.Cdrs_conns != nil {
self.CDRsConns = make([]*HaPoolConfig, len(*jsnCfg.Cdrs_conns))
for idx, jsnHaCfg := range *jsnCfg.Cdrs_conns {
self.CDRsConns[idx] = NewDfltHaPoolConfig()
self.CDRsConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnCfg.Create_cdr != nil {
self.CreateCdr = *jsnCfg.Create_cdr
@@ -281,11 +336,11 @@ func (self *SmKamConfig) loadFromJsonCfg(jsnCfg *SmKamJsonCfg) error {
return err
}
}
if jsnCfg.Connections != nil {
self.Connections = make([]*KamConnConfig, len(*jsnCfg.Connections))
for idx, jsnConnCfg := range *jsnCfg.Connections {
self.Connections[idx] = NewDfltKamConnConfig()
self.Connections[idx].loadFromJsonCfg(jsnConnCfg)
if jsnCfg.Evapi_conns != nil {
self.EvapiConns = make([]*KamConnConfig, len(*jsnCfg.Evapi_conns))
for idx, jsnConnCfg := range *jsnCfg.Evapi_conns {
self.EvapiConns[idx] = NewDfltKamConnConfig()
self.EvapiConns[idx].loadFromJsonCfg(jsnConnCfg)
}
}
return nil
@@ -311,8 +366,8 @@ func (self *OsipsConnConfig) loadFromJsonCfg(jsnCfg *OsipsConnJsonCfg) error {
type SmOsipsConfig struct {
Enabled bool
ListenUdp string
HaRater []*HaPoolConfig
HaCdrs []*HaPoolConfig
RALsConns []*HaPoolConfig
CDRsConns []*HaPoolConfig
CreateCdr bool
DebitInterval time.Duration
MinCallDuration time.Duration
@@ -329,11 +384,19 @@ func (self *SmOsipsConfig) loadFromJsonCfg(jsnCfg *SmOsipsJsonCfg) error {
if jsnCfg.Listen_udp != nil {
self.ListenUdp = *jsnCfg.Listen_udp
}
if jsnCfg.Rater != nil {
self.HaRater = []*HaPoolConfig{&HaPoolConfig{Server: *jsnCfg.Rater, Timeout: time.Duration(1) * time.Second}}
if jsnCfg.Rals_conns != nil {
self.RALsConns = make([]*HaPoolConfig, len(*jsnCfg.Rals_conns))
for idx, jsnHaCfg := range *jsnCfg.Rals_conns {
self.RALsConns[idx] = NewDfltHaPoolConfig()
self.RALsConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnCfg.Cdrs != nil {
self.HaCdrs = []*HaPoolConfig{&HaPoolConfig{Server: *jsnCfg.Cdrs, Timeout: time.Duration(1) * time.Second}}
if jsnCfg.Cdrs_conns != nil {
self.CDRsConns = make([]*HaPoolConfig, len(*jsnCfg.Cdrs_conns))
for idx, jsnHaCfg := range *jsnCfg.Cdrs_conns {
self.CDRsConns[idx] = NewDfltHaPoolConfig()
self.CDRsConns[idx].loadFromJsonCfg(jsnHaCfg)
}
}
if jsnCfg.Create_cdr != nil {
self.CreateCdr = *jsnCfg.Create_cdr

View File

@@ -29,14 +29,14 @@ func TesSmFsConfigLoadFromJsonCfg(t *testing.T) {
Enabled: utils.BoolPointer(true),
Create_cdr: utils.BoolPointer(true),
Subscribe_park: utils.BoolPointer(true),
Connections: &[]*FsConnJsonCfg{
Event_socket_conns: &[]*FsConnJsonCfg{
&FsConnJsonCfg{
Server: utils.StringPointer("1.2.3.4:8021"),
Address: utils.StringPointer("1.2.3.4:8021"),
Password: utils.StringPointer("ClueCon"),
Reconnects: utils.IntPointer(5),
},
&FsConnJsonCfg{
Server: utils.StringPointer("2.3.4.5:8021"),
Address: utils.StringPointer("2.3.4.5:8021"),
Password: utils.StringPointer("ClueCon"),
Reconnects: utils.IntPointer(5),
},
@@ -45,9 +45,9 @@ func TesSmFsConfigLoadFromJsonCfg(t *testing.T) {
eSmFsConfig := &SmFsConfig{Enabled: true,
CreateCdr: true,
SubscribePark: true,
Connections: []*FsConnConfig{
&FsConnConfig{Server: "1.2.3.4:8021", Password: "ClueCon", Reconnects: 5},
&FsConnConfig{Server: "1.2.3.4:8021", Password: "ClueCon", Reconnects: 5},
EventSocketConns: []*FsConnConfig{
&FsConnConfig{Address: "1.2.3.4:8021", Password: "ClueCon", Reconnects: 5},
&FsConnConfig{Address: "1.2.3.4:8021", Password: "ClueCon", Reconnects: 5},
},
}
smFsCfg := new(SmFsConfig)

View File

@@ -21,8 +21,8 @@ package console
import "github.com/cgrates/cgrates/apier/v1"
func init() {
c := &CmdAddTriggers{
name: "triggers_add",
c := &CmdAccountAddTriggers{
name: "account_triggers_add",
rpcMethod: "ApierV1.AddAccountActionTriggers",
rpcParams: &v1.AttrAddAccountActionTriggers{},
}
@@ -31,33 +31,33 @@ func init() {
}
// Commander implementation
type CmdAddTriggers struct {
type CmdAccountAddTriggers struct {
name string
rpcMethod string
rpcParams *v1.AttrAddAccountActionTriggers
*CommandExecuter
}
func (self *CmdAddTriggers) Name() string {
func (self *CmdAccountAddTriggers) Name() string {
return self.name
}
func (self *CmdAddTriggers) RpcMethod() string {
func (self *CmdAccountAddTriggers) RpcMethod() string {
return self.rpcMethod
}
func (self *CmdAddTriggers) RpcParams(reset bool) interface{} {
func (self *CmdAccountAddTriggers) RpcParams(reset bool) interface{} {
if reset || self.rpcParams == nil {
self.rpcParams = &v1.AttrAddAccountActionTriggers{}
}
return self.rpcParams
}
func (self *CmdAddTriggers) PostprocessRpcParams() error {
func (self *CmdAccountAddTriggers) PostprocessRpcParams() error {
return nil
}
func (self *CmdAddTriggers) RpcResult() interface{} {
func (self *CmdAccountAddTriggers) RpcResult() interface{} {
var s string
return &s
}

View File

@@ -21,8 +21,8 @@ package console
import "github.com/cgrates/cgrates/apier/v1"
func init() {
c := &CmdRemoveTriggers{
name: "triggers_remove",
c := &CmdAccountRemoveTriggers{
name: "account_triggers_remove",
rpcMethod: "ApierV1.RemoveAccountActionTriggers",
rpcParams: &v1.AttrRemoveAccountActionTriggers{},
}
@@ -31,33 +31,33 @@ func init() {
}
// Commander implementation
type CmdRemoveTriggers struct {
type CmdAccountRemoveTriggers struct {
name string
rpcMethod string
rpcParams *v1.AttrRemoveAccountActionTriggers
*CommandExecuter
}
func (self *CmdRemoveTriggers) Name() string {
func (self *CmdAccountRemoveTriggers) Name() string {
return self.name
}
func (self *CmdRemoveTriggers) RpcMethod() string {
func (self *CmdAccountRemoveTriggers) RpcMethod() string {
return self.rpcMethod
}
func (self *CmdRemoveTriggers) RpcParams(reset bool) interface{} {
func (self *CmdAccountRemoveTriggers) RpcParams(reset bool) interface{} {
if reset || self.rpcParams == nil {
self.rpcParams = &v1.AttrRemoveAccountActionTriggers{}
}
return self.rpcParams
}
func (self *CmdRemoveTriggers) PostprocessRpcParams() error {
func (self *CmdAccountRemoveTriggers) PostprocessRpcParams() error {
return nil
}
func (self *CmdRemoveTriggers) RpcResult() interface{} {
func (self *CmdAccountRemoveTriggers) RpcResult() interface{} {
var s string
return &s
}

View File

@@ -21,8 +21,8 @@ package console
import "github.com/cgrates/cgrates/apier/v1"
func init() {
c := &CmdResetTriggers{
name: "triggers_reset",
c := &CmdAccountResetTriggers{
name: "account_triggers_reset",
rpcMethod: "ApierV1.ResetAccountActionTriggers",
rpcParams: &v1.AttrRemoveAccountActionTriggers{},
}
@@ -31,33 +31,33 @@ func init() {
}
// Commander implementation
type CmdResetTriggers struct {
type CmdAccountResetTriggers struct {
name string
rpcMethod string
rpcParams *v1.AttrRemoveAccountActionTriggers
*CommandExecuter
}
func (self *CmdResetTriggers) Name() string {
func (self *CmdAccountResetTriggers) Name() string {
return self.name
}
func (self *CmdResetTriggers) RpcMethod() string {
func (self *CmdAccountResetTriggers) RpcMethod() string {
return self.rpcMethod
}
func (self *CmdResetTriggers) RpcParams(reset bool) interface{} {
func (self *CmdAccountResetTriggers) RpcParams(reset bool) interface{} {
if reset || self.rpcParams == nil {
self.rpcParams = &v1.AttrRemoveAccountActionTriggers{}
}
return self.rpcParams
}
func (self *CmdResetTriggers) PostprocessRpcParams() error {
func (self *CmdAccountResetTriggers) PostprocessRpcParams() error {
return nil
}
func (self *CmdResetTriggers) RpcResult() interface{} {
func (self *CmdAccountResetTriggers) RpcResult() interface{} {
var s string
return &s
}

View File

@@ -0,0 +1,63 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2012-2015 ITsysCOM
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 console
import "github.com/cgrates/cgrates/apier/v1"
func init() {
c := &CmdAccountSetTriggers{
name: "account_triggers_set",
rpcMethod: "ApierV1.SetAccountActionTriggers",
rpcParams: &v1.AttrSetAccountActionTriggers{},
}
commands[c.Name()] = c
c.CommandExecuter = &CommandExecuter{c}
}
// Commander implementation
type CmdAccountSetTriggers struct {
name string
rpcMethod string
rpcParams *v1.AttrSetAccountActionTriggers
*CommandExecuter
}
func (self *CmdAccountSetTriggers) Name() string {
return self.name
}
func (self *CmdAccountSetTriggers) RpcMethod() string {
return self.rpcMethod
}
func (self *CmdAccountSetTriggers) RpcParams(reset bool) interface{} {
if reset || self.rpcParams == nil {
self.rpcParams = &v1.AttrSetAccountActionTriggers{}
}
return self.rpcParams
}
func (self *CmdAccountSetTriggers) PostprocessRpcParams() error {
return nil
}
func (self *CmdAccountSetTriggers) RpcResult() interface{} {
var s string
return &s
}

View File

@@ -18,7 +18,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package console
import "github.com/cgrates/cgrates/utils"
import (
"github.com/cgrates/cgrates/apier/v2"
"github.com/cgrates/cgrates/engine"
)
func init() {
c := &CmdGetActions{
@@ -33,7 +36,7 @@ func init() {
type CmdGetActions struct {
name string
rpcMethod string
rpcParams *StringWrapper
rpcParams *v2.AttrGetActions
*CommandExecuter
}
@@ -47,7 +50,7 @@ func (self *CmdGetActions) RpcMethod() string {
func (self *CmdGetActions) RpcParams(reset bool) interface{} {
if reset || self.rpcParams == nil {
self.rpcParams = &StringWrapper{}
self.rpcParams = &v2.AttrGetActions{}
}
return self.rpcParams
}
@@ -57,6 +60,6 @@ func (self *CmdGetActions) PostprocessRpcParams() error {
}
func (self *CmdGetActions) RpcResult() interface{} {
a := make([]*utils.TPAction, 0)
a := make(map[string]engine.Actions, 0)
return &a
}

62
console/actions_remove.go Normal file
View File

@@ -0,0 +1,62 @@
/*
Real-time Charging System for Telecom & ISP environments
Copyright (C) 2012-2015 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 console
import "github.com/cgrates/cgrates/apier/v1"
func init() {
c := &CmdRemoveActions{
name: "actions_remove",
rpcMethod: "ApierV1.RemActions",
}
commands[c.Name()] = c
c.CommandExecuter = &CommandExecuter{c}
}
// Commander implementation
type CmdRemoveActions struct {
name string
rpcMethod string
rpcParams *v1.AttrRemActions
*CommandExecuter
}
func (self *CmdRemoveActions) Name() string {
return self.name
}
func (self *CmdRemoveActions) RpcMethod() string {
return self.rpcMethod
}
func (self *CmdRemoveActions) RpcParams(reset bool) interface{} {
if reset || self.rpcParams == nil {
self.rpcParams = &v1.AttrRemActions{}
}
return self.rpcParams
}
func (self *CmdRemoveActions) PostprocessRpcParams() error {
return nil
}
func (self *CmdRemoveActions) RpcResult() interface{} {
var s string
return &s
}

View File

@@ -27,7 +27,7 @@ func init() {
c := &CmdSetAliases{
name: "aliases_set",
rpcMethod: "AliasesV1.SetAlias",
rpcParams: &engine.Alias{Direction: utils.OUT},
rpcParams: &engine.AttrAddAlias{Alias: &engine.Alias{Direction: utils.OUT}},
}
commands[c.Name()] = c
c.CommandExecuter = &CommandExecuter{c}
@@ -37,7 +37,7 @@ func init() {
type CmdSetAliases struct {
name string
rpcMethod string
rpcParams *engine.Alias
rpcParams *engine.AttrAddAlias
*CommandExecuter
}
@@ -51,7 +51,7 @@ func (self *CmdSetAliases) RpcMethod() string {
func (self *CmdSetAliases) RpcParams(reset bool) interface{} {
if reset || self.rpcParams == nil {
self.rpcParams = &engine.Alias{Direction: utils.OUT}
self.rpcParams = &engine.AttrAddAlias{Alias: &engine.Alias{Direction: utils.OUT}}
}
return self.rpcParams
}

View File

@@ -23,8 +23,8 @@ import "github.com/cgrates/cgrates/apier/v1"
func init() {
c := &CmdSetTriggers{
name: "triggers_set",
rpcMethod: "ApierV1.SetAccountActionTriggers",
rpcParams: &v1.AttrSetAccountActionTriggers{},
rpcMethod: "ApierV1.SetActionTrigger",
rpcParams: &v1.AttrSetActionTrigger{},
}
commands[c.Name()] = c
c.CommandExecuter = &CommandExecuter{c}
@@ -34,7 +34,7 @@ func init() {
type CmdSetTriggers struct {
name string
rpcMethod string
rpcParams *v1.AttrSetAccountActionTriggers
rpcParams *v1.AttrSetActionTrigger
*CommandExecuter
}
@@ -48,7 +48,7 @@ func (self *CmdSetTriggers) RpcMethod() string {
func (self *CmdSetTriggers) RpcParams(reset bool) interface{} {
if reset || self.rpcParams == nil {
self.rpcParams = &v1.AttrSetAccountActionTriggers{}
self.rpcParams = &v1.AttrSetActionTrigger{}
}
return self.rpcParams
}

View File

@@ -19,48 +19,48 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package console
import (
"github.com/cgrates/cgrates/apier/v1"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
func init() {
c := &CmdUpdateAliases{
name: "aliases_update",
rpcMethod: "AliasesV1.UpdateAlias",
rpcParams: &engine.Alias{Direction: utils.OUT},
c := &CmdGetTriggers{
name: "triggers",
rpcMethod: "ApierV1.GetActionTriggers",
rpcParams: &v1.AttrGetActionTriggers{},
}
commands[c.Name()] = c
c.CommandExecuter = &CommandExecuter{c}
}
// Commander implementation
type CmdUpdateAliases struct {
type CmdGetTriggers struct {
name string
rpcMethod string
rpcParams *engine.Alias
rpcParams *v1.AttrGetActionTriggers
*CommandExecuter
}
func (self *CmdUpdateAliases) Name() string {
func (self *CmdGetTriggers) Name() string {
return self.name
}
func (self *CmdUpdateAliases) RpcMethod() string {
func (self *CmdGetTriggers) RpcMethod() string {
return self.rpcMethod
}
func (self *CmdUpdateAliases) RpcParams(reset bool) interface{} {
func (self *CmdGetTriggers) RpcParams(reset bool) interface{} {
if reset || self.rpcParams == nil {
self.rpcParams = &engine.Alias{Direction: utils.OUT}
self.rpcParams = &v1.AttrGetActionTriggers{}
}
return self.rpcParams
}
func (self *CmdUpdateAliases) PostprocessRpcParams() error {
func (self *CmdGetTriggers) PostprocessRpcParams() error {
return nil
}
func (self *CmdUpdateAliases) RpcResult() interface{} {
var s string
return &s
func (self *CmdGetTriggers) RpcResult() interface{} {
atr := engine.ActionTriggers{}
return &atr
}

View File

@@ -6,333 +6,373 @@
// This file contains the default configuration hardcoded into CGRateS.
// This is what you get when you load CGRateS with an empty configuration file.
//"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/log/cgrates/tpe", // path towards export folder for offline Tariff Plans
// "http_failed_dir": "/var/log/cgrates/http_failed", // directory path where we store failed http requests
// "default_reqtype": "*rated", // default request type to consider when missing from requests: <""|*prepaid|*postpaid|*pseudoprepaid|*rated>
// "default_category": "call", // default Type of Record to consider when missing from requests
// "default_tenant": "cgrates.org", // default Tenant to consider when missing from requests
// "default_subject": "cgrates", // default rating Subject 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
// "response_cache_ttl": "3s", // the life span of a cached response
// "internal_ttl": "2m", // maximum duration to wait for internal connections before giving up
//},
// "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/log/cgrates/tpe", // path towards export folder for offline Tariff Plans
// "http_failed_dir": "/var/log/cgrates/http_failed", // directory path where we store failed http requests
// "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
// "response_cache_ttl": "0s", // the life span of a cached response
// "internal_ttl": "2m", // maximum duration to wait for internal connections before giving up
// },
//"listen": {
// "rpc_json": "127.0.0.1:2012", // RPC JSON listening address
// "rpc_gob": "127.0.0.1:2013", // RPC GOB listening address
// "http": "127.0.0.1:2080", // HTTP listening address
//},
// "listen": {
// "rpc_json": "127.0.0.1:2012", // RPC JSON listening address
// "rpc_gob": "127.0.0.1:2013", // RPC GOB listening address
// "http": "127.0.0.1:2080", // HTTP listening address
// },
//"tariffplan_db": { // database used to store active tariff plan configuration
// "db_type": "redis", // tariffplan_db type: <redis>
// "db_host": "127.0.0.1", // tariffplan_db host address
// "db_port": 6379, // port to reach the tariffplan_db
// "db_name": "10", // tariffplan_db name to connect to
// "db_user": "", // sername to use when connecting to tariffplan_db
// "db_passwd": "", // password to use when connecting to tariffplan_db
//},
// "tariffplan_db": { // database used to store active tariff plan configuration
// "db_type": "redis", // tariffplan_db type: <redis|mongo>
// "db_host": "127.0.0.1", // tariffplan_db host address
// "db_port": 6379, // port to reach the tariffplan_db
// "db_name": "10", // tariffplan_db name to connect to
// "db_user": "", // sername to use when connecting to tariffplan_db
// "db_password": "", // password to use when connecting to tariffplan_db
// },
//"data_db": { // database used to store runtime data (eg: accounts, cdr stats)
// "db_type": "redis", // data_db type: <redis>
// "db_host": "127.0.0.1", // data_db host address
// "db_port": 6379, // data_db port to reach the database
// "db_name": "11", // data_db database name to connect to
// "db_user": "", // username to use when connecting to data_db
// "db_passwd": "", // password to use when connecting to data_db
// "load_history_size": 10, // Number of records in the load history
//},
// "data_db": { // database used to store runtime data (eg: accounts, cdr stats)
// "db_type": "redis", // data_db type: <redis|mongo>
// "db_host": "127.0.0.1", // data_db host address
// "db_port": 6379, // data_db port to reach the database
// "db_name": "11", // data_db database name to connect to
// "db_user": "", // username to use when connecting to data_db
// "db_password": "", // password to use when connecting to data_db
// "load_history_size": 10, // Number of records in the load history
// },
//"stor_db": { // database used to store offline tariff plans and CDRs
// "db_type": "mysql", // stor database type to use: <mysql|postgres>
// "db_host": "127.0.0.1", // the host to connect to
// "db_port": 3306, // the port to reach the stordb
// "db_name": "cgrates", // stor database name
// "db_user": "cgrates", // username to use when connecting to stordb
// "db_passwd": "CGRateS.org", // password to use when connecting to stordb
// "max_open_conns": 100, // maximum database connections opened
// "max_idle_conns": 10, // maximum database connections idle
//},
// "stor_db": { // database used to store offline tariff plans and CDRs
// "db_type": "mysql", // stor database type to use: <mongo|mysql|postgres>
// "db_host": "127.0.0.1", // the host to connect to
// "db_port": 3306, // the port to reach the stordb
// "db_name": "cgrates", // stor database name
// "db_user": "cgrates", // username to use when connecting to stordb
// "db_password": "CGRateS.org", // password to use when connecting to stordb
// "max_open_conns": 100, // maximum database connections opened
// "max_idle_conns": 10, // maximum database connections idle
// "cdrs_indexes": [], // indexes on cdrs table to speed up queries, used only in case of mongo
// },
//"balancer": {
// "enabled": false, // start Balancer service: <true|false>
//},
// "balancer": {
// "enabled": false, // start Balancer service: <true|false>
// },
//"rater": {
// "enabled": false, // enable Rater service: <true|false>
// "balancer": "", // register to balancer as worker: <""|internal|x.y.z.y:1234>
// "cdrstats": "", // address where to reach the cdrstats service, empty to disable stats functionality: <""|internal|x.y.z.y:1234>
// "historys": "", // address where to reach the history service, empty to disable history functionality: <""|internal|x.y.z.y:1234>
// "pubsubs": "", // address where to reach the pubusb service, empty to disable pubsub functionality: <""|internal|x.y.z.y:1234>
// "users": "", // address where to reach the user service, empty to disable user profile functionality: <""|internal|x.y.z.y:1234>
// "aliases": "", // address where to reach the aliases service, empty to disable aliases functionality: <""|internal|x.y.z.y:1234>
// "rp_subject_prefix_matching": false, // enables prefix matching for the rating profile subject
//},
// "rals": {
// "enabled": false, // enable Rater service: <true|false>
// "balancer": "", // register to balancer as worker: <""|*internal|x.y.z.y:1234>
// "cdrstats_conns": [], // address where to reach the cdrstats service, empty to disable stats functionality: <""|*internal|x.y.z.y:1234>
// "historys_conns": [], // address where to reach the history service, empty to disable history functionality: <""|*internal|x.y.z.y:1234>
// "pubsubs_conns": [], // address where to reach the pubusb service, empty to disable pubsub functionality: <""|*internal|x.y.z.y:1234>
// "users_conns": [], // address where to reach the user service, empty to disable user profile functionality: <""|*internal|x.y.z.y:1234>
// "aliases_conns": [], // address where to reach the aliases service, empty to disable aliases functionality: <""|*internal|x.y.z.y:1234>
// "rp_subject_prefix_matching": false, // enables prefix matching for the rating profile subject
// "lcr_subject_prefix_matching": false // enables prefix matching for the lcr subject
// },
//"scheduler": {
// "enabled": false, // start Scheduler service: <true|false>
//},
// "scheduler": {
// "enabled": false, // start Scheduler service: <true|false>
// },
//"cdrs": {
// "enabled": false, // start the CDR Server service: <true|false>
// "extra_fields": [], // extra fields to store in CDRs for non-generic CDRs
// "store_cdrs": true, // store cdrs in storDb
// "rater": "internal", // address where to reach the Rater for cost calculation, empty to disable functionality: <""|internal|x.y.z.y:1234>
// "pubsubs": "", // address where to reach the pubusb service, empty to disable pubsub functionality: <""|internal|x.y.z.y:1234>
// "users": "", // address where to reach the user service, empty to disable user profile functionality: <""|internal|x.y.z.y:1234>
// "aliases": "", // address where to reach the aliases service, empty to disable aliases functionality: <""|internal|x.y.z.y:1234>
// "cdrstats": "", // address where to reach the cdrstats service, empty to disable stats functionality<""|internal|x.y.z.y:1234>
// "cdr_replication":[], // replicate the raw CDR to a number of servers
//},
// "cdrs": {
// "enabled": false, // start the CDR Server service: <true|false>
// "extra_fields": [], // extra fields to store in CDRs for non-generic CDRs
// "store_cdrs": true, // store cdrs in storDb
// "rals_conns": [
// {"address": "*internal"} // address where to reach the Rater for cost calculation, empty to disable functionality: <""|*internal|x.y.z.y:1234>
// ],
// "pubsubs_conns": [], // address where to reach the pubusb service, empty to disable pubsub functionality: <""|*internal|x.y.z.y:1234>
// "users_conns": [], // address where to reach the user service, empty to disable user profile functionality: <""|*internal|x.y.z.y:1234>
// "aliases_conns": [], // address where to reach the aliases service, empty to disable aliases functionality: <""|*internal|x.y.z.y:1234>
// "cdrstats_conns": [], // address where to reach the cdrstats service, empty to disable stats functionality<""|*internal|x.y.z.y:1234>
// "cdr_replication":[] // replicate the raw CDR to a number of servers
// },
//"cdrstats": {
// "enabled": false, // starts the cdrstats service: <true|false>
// "save_interval": "1m", // interval to save changed stats into dataDb storage
//},
// "cdrstats": {
// "enabled": false, // starts the cdrstats service: <true|false>
// "save_interval": "1m", // interval to save changed stats into dataDb storage
// },
//"cdre": {
// "*default": {
// "cdr_format": "csv", // exported CDRs format <csv>
// "field_separator": ",",
// "data_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from KBytes to Bytes)
// "sms_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from SMS unit to call duration in some billing systems)
// "generic_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from GENERIC unit to call duration in some billing systems)
// "cost_multiply_factor": 1, // multiply cost before export, eg: add VAT
// "cost_rounding_decimals": -1, // rounding decimals for Cost values. -1 to disable rounding
// "cost_shift_digits": 0, // shift digits in the cost on export (eg: convert from EUR to cents)
// "mask_destination_id": "MASKED_DESTINATIONS", // destination id containing called addresses to be masked on export
// "mask_length": 0, // length of the destination suffix to be masked
// "export_dir": "/var/log/cgrates/cdre", // path where the exported CDRs will be placed
// "header_fields": [], // template of the exported header fields
// "content_fields": [ // template of the exported content fields
// {"tag": "CgrId", "field_id": "CgrId", "type": "*composed", "value": "CgrId"},
// {"tag":"RunId", "field_id": "MediationRunId", "type": "*composed", "value": "MediationRunId"},
// {"tag":"Tor", "field_id": "TOR", "type": "*composed", "value": "TOR"},
// {"tag":"AccId", "field_id": "AccId", "type": "*composed", "value": "AccId"},
// {"tag":"ReqType", "field_id": "ReqType", "type": "*composed", "value": "ReqType"},
// {"tag":"Direction", "field_id": "Direction", "type": "*composed", "value": "Direction"},
// {"tag":"Tenant", "field_id": "Tenant", "type": "*composed", "value": "Tenant"},
// {"tag":"Category", "field_id": "Category", "type": "*composed", "value": "Category"},
// {"tag":"Account", "field_id": "Account", "type": "*composed", "value": "Account"},
// {"tag":"Subject", "field_id": "Subject", "type": "*composed", "value": "Subject"},
// {"tag":"Destination", "field_id": "Destination", "type": "*composed", "value": "Destination"},
// {"tag":"SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "SetupTime", "layout": "2006-01-02T15:04:05Z07:00"},
// {"tag":"AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"},
// {"tag":"Usage", "field_id": "Usage", "type": "*composed", "value": "Usage"},
// {"tag":"Cost", "field_id": "Cost", "type": "*composed", "value": "Cost"},
// ],
// "trailer_fields": [], // template of the exported trailer fields
// }
//},
// "cdre": {
// "*default": {
// "cdr_format": "csv", // exported CDRs format <csv>
// "field_separator": ",",
// "data_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from KBytes to Bytes)
// "sms_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from SMS unit to call duration in some billing systems)
// "mms_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from MMS unit to call duration in some billing systems)
// "generic_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from GENERIC unit to call duration in some billing systems)
// "cost_multiply_factor": 1, // multiply cost before export, eg: add VAT
// "cost_rounding_decimals": -1, // rounding decimals for Cost values. -1 to disable rounding
// "cost_shift_digits": 0, // shift digits in the cost on export (eg: convert from EUR to cents)
// "mask_destination_id": "MASKED_DESTINATIONS", // destination id containing called addresses to be masked on export
// "mask_length": 0, // length of the destination suffix to be masked
// "export_folder": "/var/log/cgrates/cdre", // path where the exported CDRs will be placed
// "header_fields": [], // template of the exported header fields
// "content_fields": [ // template of the exported content fields
// {"tag": "CGRID", "field_id": "CGRID", "type": "*composed", "value": "CGRID"},
// {"tag":"RunID", "field_id": "RunID", "type": "*composed", "value": "RunID"},
// {"tag":"TOR", "field_id": "ToR", "type": "*composed", "value": "ToR"},
// {"tag":"OriginID", "field_id": "OriginID", "type": "*composed", "value": "OriginID"},
// {"tag":"RequestType", "field_id": "RequestType", "type": "*composed", "value": "RequestType"},
// {"tag":"Direction", "field_id": "Direction", "type": "*composed", "value": "Direction"},
// {"tag":"Tenant", "field_id": "Tenant", "type": "*composed", "value": "Tenant"},
// {"tag":"Category", "field_id": "Category", "type": "*composed", "value": "Category"},
// {"tag":"Account", "field_id": "Account", "type": "*composed", "value": "Account"},
// {"tag":"Subject", "field_id": "Subject", "type": "*composed", "value": "Subject"},
// {"tag":"Destination", "field_id": "Destination", "type": "*composed", "value": "Destination"},
// {"tag":"SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "SetupTime", "layout": "2006-01-02T15:04:05Z07:00"},
// {"tag":"AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"},
// {"tag":"Usage", "field_id": "Usage", "type": "*composed", "value": "Usage"},
// {"tag":"Cost", "field_id": "Cost", "type": "*composed", "value": "Cost"},
// ],
// "trailer_fields": [], // template of the exported trailer fields
// }
// },
//"cdrc": {
// "*default": {
// "enabled": false, // enable CDR client functionality
// "dry_run": false, // do not send the CDRs to CDRS, just parse them
// "cdrs": "internal", // address where to reach CDR server. <internal|x.y.z.y:1234>
// "cdr_format": "csv", // CDR file format <csv|freeswitch_csv|fwv|opensips_flatstore>
// "field_separator": ",", // separator used in case of csv files
// "timezone": "", // timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB>
// "run_delay": 0, // sleep interval in seconds between consecutive runs, 0 to use automation via inotify
// "max_open_files": 1024, // maximum simultaneous files to process, 0 for unlimited
// "data_usage_multiply_factor": 1024, // conversion factor for data usage
// "cdr_in_dir": "/var/log/cgrates/cdrc/in", // absolute path towards the directory where the CDRs are stored
// "cdr_out_dir": "/var/log/cgrates/cdrc/out", // absolute path towards the directory where processed CDRs will be moved
// "failed_calls_prefix": "missed_calls", // used in case of flatstore CDRs to avoid searching for BYE records
// "cdr_source_id": "freeswitch_csv", // free form field, tag identifying the source of the CDRs within CDRS database
// "cdr_filter": "", // filter CDR records to import
// "continue_on_success": false, // continue to the next template if executed
// "partial_record_cache": "10s", // duration to cache partial records when not pairing
// "header_fields": [], // template of the import header fields
// "content_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value
// {"tag": "tor", "field_id": "TOR", "type": "*composed", "value": "2", "mandatory": true},
// {"tag": "accid", "field_id": "AccId", "type": "*composed", "value": "3", "mandatory": true},
// {"tag": "reqtype", "field_id": "ReqType", "type": "*composed", "value": "4", "mandatory": true},
// {"tag": "direction", "field_id": "Direction", "type": "*composed", "value": "5", "mandatory": true},
// {"tag": "tenant", "field_id": "Tenant", "type": "*composed", "value": "6", "mandatory": true},
// {"tag": "category", "field_id": "Category", "type": "*composed", "value": "7", "mandatory": true},
// {"tag": "account", "field_id": "Account", "type": "*composed", "value": "8", "mandatory": true},
// {"tag": "subject", "field_id": "Subject", "type": "*composed", "value": "9", "mandatory": true},
// {"tag": "destination", "field_id": "Destination", "type": "*composed", "value": "10", "mandatory": true},
// {"tag": "setup_time", "field_id": "SetupTime", "type": "*composed", "value": "11", "mandatory": true},
// {"tag": "answer_time", "field_id": "AnswerTime", "type": "*composed", "value": "12", "mandatory": true},
// {"tag": "usage", "field_id": "Usage", "type": "*composed", "value": "13", "mandatory": true},
// ],
// "trailer_fields": [], // template of the import trailer fields
// }
//},
//"sm_generic": {
// "enabled": false, // starts SessionManager service: <true|false>
// "listen_bijson": "127.0.0.1:2014", // address where to listen for bidirectional JSON-RPC requests
// "rater": "internal", // address where to reach the Rater <""|internal|127.0.0.1:2013>
// "cdrs": "internal", // address where to reach CDR Server <""|internal|x.y.z.y:1234>
// "debit_interval": "0s", // interval to perform debits on.
// "min_call_duration": "0s", // only authorize calls with allowed duration higher than this
// "max_call_duration": "3h", // maximum call duration a prepaid call can last
//},
// "cdrc": [
// {
// "id": "*default", // identifier of the CDRC runner
// "enabled": false, // enable CDR client functionality
// "dry_run": false, // do not send the CDRs to CDRS, just parse them
// "cdrs_conns": [
// {"address": "*internal"} // address where to reach CDR server. <*internal|x.y.z.y:1234>
// ],
// "cdr_format": "csv", // CDR file format <csv|freeswitch_csv|fwv|opensips_flatstore>
// "field_separator": ",", // separator used in case of csv files
// "timezone": "", // timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB>
// "run_delay": 0, // sleep interval in seconds between consecutive runs, 0 to use automation via inotify
// "max_open_files": 1024, // maximum simultaneous files to process, 0 for unlimited
// "data_usage_multiply_factor": 1024, // conversion factor for data usage
// "cdr_in_dir": "/var/log/cgrates/cdrc/in", // absolute path towards the directory where the CDRs are stored
// "cdr_out_dir": "/var/log/cgrates/cdrc/out", // absolute path towards the directory where processed CDRs will be moved
// "failed_calls_prefix": "missed_calls", // used in case of flatstore CDRs to avoid searching for BYE records
// "cdr_source_id": "freeswitch_csv", // free form field, tag identifying the source of the CDRs within CDRS database
// "cdr_filter": "", // filter CDR records to import
// "continue_on_success": false, // continue to the next template if executed
// "partial_record_cache": "10s", // duration to cache partial records when not pairing
// "header_fields": [], // template of the import header fields
// "content_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value
// {"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "2", "mandatory": true},
// {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "3", "mandatory": true},
// {"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "4", "mandatory": true},
// {"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "5", "mandatory": true},
// {"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "6", "mandatory": true},
// {"tag": "Category", "field_id": "Category", "type": "*composed", "value": "7", "mandatory": true},
// {"tag": "Account", "field_id": "Account", "type": "*composed", "value": "8", "mandatory": true},
// {"tag": "Subject", "field_id": "Subject", "type": "*composed", "value": "9", "mandatory": true},
// {"tag": "Destination", "field_id": "Destination", "type": "*composed", "value": "10", "mandatory": true},
// {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "11", "mandatory": true},
// {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "12", "mandatory": true},
// {"tag": "Usage", "field_id": "Usage", "type": "*composed", "value": "13", "mandatory": true},
// ],
// "trailer_fields": [], // template of the import trailer fields
// },
// ],
//"sm_freeswitch": {
// "enabled": false, // starts SessionManager service: <true|false>
// "rater": "internal", // address where to reach the Rater <""|internal|127.0.0.1:2013>
// "cdrs": "internal", // address where to reach CDR Server, empty to disable CDR capturing <""|internal|x.y.z.y:1234>
// "create_cdr": false, // create CDR out of events and sends them to CDRS component
// "extra_fields": [], // extra fields to store in auth/CDRs when creating them
// "debit_interval": "10s", // interval to perform debits on.
// "min_call_duration": "0s", // only authorize calls with allowed duration higher than this
// "max_call_duration": "3h", // maximum call duration a prepaid call can last
// "min_dur_low_balance": "5s", // threshold which will trigger low balance warnings for prepaid calls (needs to be lower than debit_interval)
// "low_balance_ann_file": "", // file to be played when low balance is reached for prepaid calls
// "empty_balance_context": "", // if defined, prepaid calls will be transfered to this context on empty balance
// "empty_balance_ann_file": "", // file to be played before disconnecting prepaid calls on empty balance (applies only if no context defined)
// "subscribe_park": true, // subscribe via fsock to receive park events
// "channel_sync_interval": "5m", // sync channels with freeswitch regularly
// "connections":[ // instantiate connections to multiple FreeSWITCH servers
// {"server": "127.0.0.1:8021", "password": "ClueCon", "reconnects": 5}
// ],
//},
// "sm_generic": {
// "enabled": false, // starts SessionManager service: <true|false>
// "listen_bijson": "127.0.0.1:2014", // address where to listen for bidirectional JSON-RPC requests
// "rals_conns": [
// {"address": "*internal"} // address where to reach the Rater <""|*internal|127.0.0.1:2013>
// ],
// "cdrs_conns": [
// {"address": "*internal"} // address where to reach CDR Server, empty to disable CDR capturing <*internal|x.y.z.y:1234>
// ],
// "debit_interval": "0s", // interval to perform debits on.
// "min_call_duration": "0s", // only authorize calls with allowed duration higher than this
// "max_call_duration": "3h", // maximum call duration a prepaid call can last
// "session_ttl": "0s", // time after a session with no updates is terminated, not defined by default
//"session_ttl_last_used": "", // tweak LastUsed for sessions timing-out, not defined by default
//"session_ttl_usage": "", // tweak Usage for sessions timing-out, not defined by default
// },
//"sm_kamailio": {
// "enabled": false, // starts SessionManager service: <true|false>
// "rater": "internal", // address where to reach the Rater <""|internal|127.0.0.1:2013>
// "cdrs": "internal", // address where to reach CDR Server, empty to disable CDR capturing <""|internal|x.y.z.y:1234>
// "create_cdr": false, // create CDR out of events and sends them to CDRS component
// "debit_interval": "10s", // interval to perform debits on.
// "min_call_duration": "0s", // only authorize calls with allowed duration higher than this
// "max_call_duration": "3h", // maximum call duration a prepaid call can last
// "connections":[ // instantiate connections to multiple Kamailio servers
// {"evapi_addr": "127.0.0.1:8448", "reconnects": 5}
// ],
//},
// "sm_freeswitch": {
// "enabled": false, // starts SessionManager service: <true|false>
// "rals_conns": [
// {"address": "*internal"} // address where to reach the Rater <""|*internal|127.0.0.1:2013>
// ],
// "cdrs_conns": [
// {"address": "*internal"} // address where to reach CDR Server, empty to disable CDR capturing <*internal|x.y.z.y:1234>
// ],
// "create_cdr": false, // create CDR out of events and sends them to CDRS component
// "extra_fields": [], // extra fields to store in auth/CDRs when creating them
// "debit_interval": "10s", // interval to perform debits on.
// "min_call_duration": "0s", // only authorize calls with allowed duration higher than this
// "max_call_duration": "3h", // maximum call duration a prepaid call can last
// "min_dur_low_balance": "5s", // threshold which will trigger low balance warnings for prepaid calls (needs to be lower than debit_interval)
// "low_balance_ann_file": "", // file to be played when low balance is reached for prepaid calls
// "empty_balance_context": "", // if defined, prepaid calls will be transfered to this context on empty balance
// "empty_balance_ann_file": "", // file to be played before disconnecting prepaid calls on empty balance (applies only if no context defined)
// "subscribe_park": true, // subscribe via fsock to receive park events
// "channel_sync_interval": "5m", // sync channels with freeswitch regularly
// "max_wait_connection": "2s", // maximum duration to wait for a connection to be retrieved from the pool
// "event_socket_conns":[ // instantiate connections to multiple FreeSWITCH servers
// {"address": "127.0.0.1:8021", "password": "ClueCon", "reconnects": 5}
// ],
// },
//"sm_opensips": {
// "enabled": false, // starts SessionManager service: <true|false>
// "listen_udp": "127.0.0.1:2020", // address where to listen for datagram events coming from OpenSIPS
// "rater": "internal", // address where to reach the Rater <""|internal|127.0.0.1:2013>
// "cdrs": "internal", // address where to reach CDR Server, empty to disable CDR capturing <""|internal|x.y.z.y:1234>
// "reconnects": 5, // number of reconnects if connection is lost
// "create_cdr": false, // create CDR out of events and sends them to CDRS component
// "debit_interval": "10s", // interval to perform debits on.
// "min_call_duration": "0s", // only authorize calls with allowed duration higher than this
// "max_call_duration": "3h", // maximum call duration a prepaid call can last
// "events_subscribe_interval": "60s", // automatic events subscription to OpenSIPS, 0 to disable it
// "mi_addr": "127.0.0.1:8020", // address where to reach OpenSIPS MI to send session disconnects
//},
// "sm_kamailio": {
// "enabled": false, // starts SessionManager service: <true|false>
// "rals_conns": [
// {"address": "*internal"} // address where to reach the Rater <""|*internal|127.0.0.1:2013>
// ],
// "cdrs_conns": [
// {"address": "*internal"} // address where to reach CDR Server, empty to disable CDR capturing <*internal|x.y.z.y:1234>
// ],
// "create_cdr": false, // create CDR out of events and sends them to CDRS component
// "debit_interval": "10s", // interval to perform debits on.
// "min_call_duration": "0s", // only authorize calls with allowed duration higher than this
// "max_call_duration": "3h", // maximum call duration a prepaid call can last
// "evapi_conns":[ // instantiate connections to multiple Kamailio servers
// {"address": "127.0.0.1:8448", "reconnects": 5}
// ],
// },
//"diameter_agent": {
// "enabled": false, // enables the diameter agent: <true|false>
// "listen": "127.0.0.1:3868", // address where to listen for diameter requests <x.y.z.y:1234>
// "dictionaries_dir": "/usr/share/cgrates/diameter/dict/", // path towards directory holding additional dictionaries to load
// "sm_generic": "internal", // connection towards SMG component for session management
// "timezone": "", // timezone for timestamps where not specified, empty for general defaults <""|UTC|Local|$IANA_TZ_DB>
// "origin_host": "CGR-DA", // diameter Origin-Host AVP used in replies
// "origin_realm": "cgrates.org", // diameter Origin-Realm AVP used in replies
// "vendor_id": 0, // diameter Vendor-Id AVP used in replies
// "product_name": "CGRateS", // diameter Product-Name AVP used in replies
// "request_processors": [
// {
// "id": "*default", // formal identifier of this processor
// "dry_run": false, // do not send the CDRs to CDRS, just parse them
// "request_filter": "Subscription-Id>Subscription-Id-Type(0)", // filter requests processed by this processor
// "continue_on_success": false, // continue to the next template if executed
// "content_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value
// {"tag": "tor", "field_id": "TOR", "type": "*composed", "value": "^*voice", "mandatory": true},
// {"tag": "accid", "field_id": "AccId", "type": "*composed", "value": "Session-Id", "mandatory": true},
// {"tag": "reqtype", "field_id": "ReqType", "type": "*composed", "value": "^*users", "mandatory": true},
// {"tag": "direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true},
// {"tag": "tenant", "field_id": "Tenant", "type": "*composed", "value": "^*users", "mandatory": true},
// {"tag": "category", "field_id": "Category", "type": "*composed", "value": "^call_;~Service-Information>IN-Information>Calling-Vlr-Number:s/^$/33000/;~Service-Information>IN-Information>Calling-Vlr-Number:s/^(\\d{5})/${1}/", "mandatory": true},
// {"tag": "account", "field_id": "Account", "type": "*composed", "value": "^*users", "mandatory": true},
// {"tag": "subject", "field_id": "Subject", "type": "*composed", "value": "^*users", "mandatory": true},
// {"tag": "destination", "field_id": "Destination", "type": "*composed", "value": "Service-Information>IN-Information>Real-Called-Number", "mandatory": true},
// {"tag": "setup_time", "field_id": "SetupTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true},
// {"tag": "answer_time", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true},
// {"tag": "usage", "field_id": "Usage", "type": "*composed", "value": "Requested-Service-Unit>CC-Time", "mandatory": true},
// {"tag": "subscriber_id", "field_id": "SubscriberId", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "mandatory": true},
// ],
// },
// ],
//},
// "sm_opensips": {
// "enabled": false, // starts SessionManager service: <true|false>
// "listen_udp": "127.0.0.1:2020", // address where to listen for datagram events coming from OpenSIPS
// "rals_conns": [
// {"address": "*internal"} // address where to reach the Rater <""|*internal|127.0.0.1:2013>
// ],
// "cdrs_conns": [
// {"address": "*internal"} // address where to reach CDR Server, empty to disable CDR capturing <*internal|x.y.z.y:1234>
// ],
// "reconnects": 5, // number of reconnects if connection is lost
// "create_cdr": false, // create CDR out of events and sends it to CDRS component
// "debit_interval": "10s", // interval to perform debits on.
// "min_call_duration": "0s", // only authorize calls with allowed duration higher than this
// "max_call_duration": "3h", // maximum call duration a prepaid call can last
// "events_subscribe_interval": "60s", // automatic events subscription to OpenSIPS, 0 to disable it
// "mi_addr": "127.0.0.1:8020", // address where to reach OpenSIPS MI to send session disconnects
// },
//"historys": {
// "enabled": false, // starts History service: <true|false>.
// "history_dir": "/var/log/cgrates/history", // location on disk where to store history files.
// "save_interval": "1s", // interval to save changed cache into .git archive
//},
// "diameter_agent": {
// "enabled": false, // enables the diameter agent: <true|false>
// "listen": "127.0.0.1:3868", // address where to listen for diameter requests <x.y.z.y:1234>
// "dictionaries_dir": "/usr/share/cgrates/diameter/dict/", // path towards directory holding additional dictionaries to load
// "sm_generic_conns": [
// {"address": "*internal"} // connection towards SMG component for session management
// ],
// "pubsubs_conns": [], // address where to reach the pubusb service, empty to disable pubsub functionality: <""|*internal|x.y.z.y:1234>
// "create_cdr": true, // create CDR out of CCR terminate and send it to SMG component
// "debit_interval": "5m", // interval for CCR updates
// "timezone": "", // timezone for timestamps where not specified, empty for general defaults <""|UTC|Local|$IANA_TZ_DB>
// "dialect": "huawei", // the diameter dialect used in the communication, supported: <huawei>
// "origin_host": "CGR-DA", // diameter Origin-Host AVP used in replies
// "origin_realm": "cgrates.org", // diameter Origin-Realm AVP used in replies
// "vendor_id": 0, // diameter Vendor-Id AVP used in replies
// "product_name": "CGRateS", // diameter Product-Name AVP used in replies
// "request_processors": [
// {
// "id": "*default", // formal identifier of this processor
// "dry_run": false, // do not send the events to SMG, just log them
// "publish_event": false, // if enabled, it will publish internal event to pubsub
// "request_filter": "Subscription-Id>Subscription-Id-Type(0)", // filter requests processed by this processor
// "flags": [], // flags to influence processing behavior
// "continue_on_success": false, // continue to the next template if executed
// "append_cca": true, // when continuing will append cca fields to the previous ones
// "ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value
// {"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*voice", "mandatory": true},
// {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Session-Id", "mandatory": true},
// {"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "^*users", "mandatory": true},
// {"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true},
// {"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "^*users", "mandatory": true},
// {"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^call", "mandatory": true},
// {"tag": "Account", "field_id": "Account", "type": "*composed", "value": "^*users", "mandatory": true},
// {"tag": "Subject", "field_id": "Subject", "type": "*composed", "value": "^*users", "mandatory": true},
// {"tag": "Destination", "field_id": "Destination", "type": "*composed", "value": "Service-Information>IN-Information>Real-Called-Number", "mandatory": true},
// {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true},
// {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true},
// {"tag": "Usage", "field_id": "Usage", "type": "*handler", "handler_id": "*ccr_usage", "mandatory": true},
// {"tag": "SubscriberID", "field_id": "SubscriberId", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "mandatory": true},
// ],
// "cca_fields":[ // fields returned in CCA
// {"tag": "GrantedUnits", "field_id": "Granted-Service-Unit>CC-Time", "type": "*handler", "handler_id": "*cca_usage", "mandatory": true},
// ],
// },
// ],
// },
//"pubsubs": {
// "enabled": false, // starts PubSub service: <true|false>.
//},
// "historys": {
// "enabled": false, // starts History service: <true|false>.
// "history_dir": "/var/log/cgrates/history", // location on disk where to store history files.
// "save_interval": "1s", // interval to save changed cache into .git archive
// },
//"aliases": {
// "enabled": false, // starts Aliases service: <true|false>.
//},
// "pubsubs": {
// "enabled": false, // starts PubSub service: <true|false>.
// },
//"users": {
// "enabled": false, // starts User service: <true|false>.
// "indexes": [], // user profile field indexes
//},
// "aliases": {
// "enabled": false, // starts Aliases service: <true|false>.
// },
//"mailer": {
// "server": "localhost", // the server to use when sending emails out
// "auth_user": "cgrates", // authenticate to email server using this user
// "auth_passwd": "CGRateS.org", // authenticate to email server with this password
// "from_address": "cgr-mailer@localhost.localdomain" // from address used when sending emails out
//},
// "users": {
// "enabled": false, // starts User service: <true|false>.
// "indexes": [], // user profile field indexes
// },
//"suretax": {
// "url": "", // API url
// "client_number": "", // client number, provided by SureTax
// "validation_key": "", // validation key provided by SureTax
// "business_unit": "", // clients Business Unit
// "timezone": "Local", // convert the time of the events to this timezone before sending request out <UTC|Local|$IANA_TZ_DB>
// "include_local_cost": false, // sum local calculated cost with tax one in final cost
// "return_file_code": "0", // default or Quote purposes <0|Q>
// "response_group": "03", // determines how taxes are grouped for the response <03|13>
// "response_type": "D4", // determines the granularity of taxes and (optionally) the decimal precision for the tax calculations and amounts in the response
// "regulatory_code": "03", // provider type
// "client_tracking": "CgrId", // template extracting client information out of StoredCdr; <$RSRFields>
// "customer_number": "Subject", // template extracting customer number out of StoredCdr; <$RSRFields>
// "orig_number": "Subject", // template extracting origination number out of StoredCdr; <$RSRFields>
// "term_number": "Destination", // template extracting termination number out of StoredCdr; <$RSRFields>
// "bill_to_number": "", // template extracting billed to number out of StoredCdr; <$RSRFields>
// "zipcode": "", // template extracting billing zip code out of StoredCdr; <$RSRFields>
// "plus4": "", // template extracting billing zip code extension out of StoredCdr; <$RSRFields>
// "p2pzipcode": "", // template extracting secondary zip code out of StoredCdr; <$RSRFields>
// "p2pplus4": "", // template extracting secondary zip code extension out of StoredCdr; <$RSRFields>
// "units": "^1", // template extracting number of “lines” or unique charges contained within the revenue out of StoredCdr; <$RSRFields>
// "unit_type": "^00", // template extracting number of unique access lines out of StoredCdr; <$RSRFields>
// "tax_included": "^0", // template extracting tax included in revenue out of StoredCdr; <$RSRFields>
// "tax_situs_rule": "^04", // template extracting tax situs rule out of StoredCdr; <$RSRFields>
// "trans_type_code": "^010101", // template extracting transaction type indicator out of StoredCdr; <$RSRFields>
// "sales_type_code": "^R", // template extracting sales type code out of StoredCdr; <$RSRFields>
// "tax_exemption_code_list": "", // template extracting tax exemption code list out of StoredCdr; <$RSRFields>
//},
// "mailer": {
// "server": "localhost", // the server to use when sending emails out
// "auth_user": "cgrates", // authenticate to email server using this user
// "auth_password": "CGRateS.org", // authenticate to email server with this password
// "from_address": "cgr-mailer@localhost.localdomain" // from address used when sending emails out
// },
}
// "suretax": {
// "url": "", // API url
// "client_number": "", // client number, provided by SureTax
// "validation_key": "", // validation key provided by SureTax
// "business_unit": "", // clients Business Unit
// "timezone": "Local", // convert the time of the events to this timezone before sending request out <UTC|Local|$IANA_TZ_DB>
// "include_local_cost": false, // sum local calculated cost with tax one in final cost
// "return_file_code": "0", // default or Quote purposes <0|Q>
// "response_group": "03", // determines how taxes are grouped for the response <03|13>
// "response_type": "D4", // determines the granularity of taxes and (optionally) the decimal precision for the tax calculations and amounts in the response
// "regulatory_code": "03", // provider type
// "client_tracking": "CGRID", // template extracting client information out of StoredCdr; <$RSRFields>
// "customer_number": "Subject", // template extracting customer number out of StoredCdr; <$RSRFields>
// "orig_number": "Subject", // template extracting origination number out of StoredCdr; <$RSRFields>
// "term_number": "Destination", // template extracting termination number out of StoredCdr; <$RSRFields>
// "bill_to_number": "", // template extracting billed to number out of StoredCdr; <$RSRFields>
// "zipcode": "", // template extracting billing zip code out of StoredCdr; <$RSRFields>
// "plus4": "", // template extracting billing zip code extension out of StoredCdr; <$RSRFields>
// "p2pzipcode": "", // template extracting secondary zip code out of StoredCdr; <$RSRFields>
// "p2pplus4": "", // template extracting secondary zip code extension out of StoredCdr; <$RSRFields>
// "units": "^1", // template extracting number of “lines” or unique charges contained within the revenue out of StoredCdr; <$RSRFields>
// "unit_type": "^00", // template extracting number of unique access lines out of StoredCdr; <$RSRFields>
// "tax_included": "^0", // template extracting tax included in revenue out of StoredCdr; <$RSRFields>
// "tax_situs_rule": "^04", // template extracting tax situs rule out of StoredCdr; <$RSRFields>
// "trans_type_code": "^010101", // template extracting transaction type indicator out of StoredCdr; <$RSRFields>
// "sales_type_code": "^R", // template extracting sales type code out of StoredCdr; <$RSRFields>
// "tax_exemption_code_list": "", // template extracting tax exemption code list out of StoredCdr; <$RSRFields>
// },
}

View File

@@ -10,11 +10,17 @@
"http": ":2080", // HTTP listening address
},
"rater": {
"rals": {
"enabled": true, // enable Rater service: <true|false>
"pubsubs": "internal",
"users": "internal",
"aliases": "internal",
"pubsubs_conns": [
{"address": "*internal"}
],
"users_conns": [
{"address": "*internal"}
],
"aliases_conns": [
{"address": "*internal"}
],
},
"scheduler": {

View File

@@ -10,9 +10,11 @@
"http": ":2080", // HTTP listening address
},
"rater": {
"rals": {
"enabled": true, // enable Rater service: <true|false>
"aliases": "internal"
"aliases_conns": [
{"address": "*internal"}
],
},
"scheduler": {
@@ -21,7 +23,9 @@
"cdrs": {
"enabled": true, // start the CDR Server service: <true|false>
"rater": "internal", // address where to reach the Rater for cost calculation, empty to disable functionality: <""|internal|x.y.z.y:1234>
"rals_conns": [
{"address": "*internal"} // address where to reach the Rater <""|*internal|127.0.0.1:2013>
],
},
"aliases": {
@@ -34,4 +38,9 @@
}
},
"cdrstats": {
"enabled": true, // starts the cdrstats service: <true|false>
"save_interval": "0s", // interval to save changed stats into dataDb storage
},
}

View File

@@ -0,0 +1,57 @@
{
// Real-time Charging System 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.
"rals": {
"enabled": true // so we can query CDRs
},
"cdrs": {
"enabled": true,
"rals_conns": [], // no rating support, just *raw CDR testing
},
"cdrc": [
{
"id": "*default",
"enabled": true,
"cdr_in_dir": "/tmp/cdrctests/csvit1/in",
"cdr_out_dir": "/tmp/cdrctests/csvit1/out",
"cdr_source_id": "csvit1",
},
{
"id": "*CSVit2", // identifier of the CDRC runner
"enabled": true, // enable CDR client functionality
"field_separator": ";",
"cdr_in_dir": "/tmp/cdrctests/csvit2/in", // absolute path towards the directory where the CDRs are stored
"cdr_out_dir": "/tmp/cdrctests/csvit2/out", // absolute path towards the directory where processed CDRs will be moved
"cdr_source_id": "csvit2", // free form field, tag identifying the source of the CDRs within CDRS database
"content_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value
{"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*voice", "mandatory": true},
{"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "0", "mandatory": true},
{"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "1", "mandatory": true},
{"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true},
{"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "2", "mandatory": true},
{"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^call", "mandatory": true},
{"tag": "Account", "field_id": "Account", "type": "*composed", "value": "3", "mandatory": true},
{"tag": "Subject", "field_id": "Subject", "type": "*composed", "value": "3", "mandatory": true},
{"tag": "Destination", "field_id": "Destination", "type": "*composed", "value": "~4:s/0([1-9]\\d+)/+49${1}/", "mandatory": true},
{"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "5", "mandatory": true},
{"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "5", "mandatory": true},
{"tag": "Usage", "field_id": "Usage", "type": "*composed", "value": "6", "mandatory": true},
{"tag": "HDRExtra3", "field_id": "HDRExtra3", "type": "*composed", "value": "6", "mandatory": true},
{"tag": "HDRExtra2", "field_id": "HDRExtra2", "type": "*composed", "value": "6", "mandatory": true},
{"tag": "HDRExtra1", "field_id": "HDRExtra1", "type": "*composed", "value": "6", "mandatory": true},
],
},
],
}

View File

@@ -7,7 +7,7 @@
// This is what you get when you load CGRateS with an empty configuration file.
"rater": {
"rals": {
"enabled": true, // enable Rater service: <true|false>
},
@@ -22,10 +22,13 @@
},
"cdrc": {
"FLATSTORE": {
"cdrc": [
{
"id": "FLATSTORE",
"enabled": true, // enable CDR client functionality
"cdrs": "internal", // address where to reach CDR server. <internal|x.y.z.y:1234>
"cdrs_conns": [
{"address": "*internal"} // address where to reach CDR server. <*internal|x.y.z.y:1234>
],
"cdr_format": "opensips_flatstore", // CDR file format <csv|freeswitch_csv|fwv|opensips_flatstore>
"field_separator": "|", // separator used in case of csv files
"run_delay": 0, // sleep interval in seconds between consecutive runs, 0 to use automation via inotify
@@ -54,6 +57,6 @@
{"tag": "DialogId", "cdr_field_id": "DialogId", "type": "cdrfield", "value": "11"},
],
},
},
],
}

View File

@@ -7,7 +7,7 @@
// This is what you get when you load CGRateS with an empty configuration file.
"rater": {
"rals": {
"enabled": true, // enable Rater service: <true|false>
},
@@ -22,11 +22,14 @@
},
"cdrc": {
"FWV1": {
"cdrc": [
{
"id": "FWV1",
"enabled": true, // enable CDR client functionality
"dry_run": true,
"cdrs": "internal", // address where to reach CDR server. <internal|x.y.z.y:1234>
"cdrs_conns": [
{"address": "*internal"} // address where to reach CDR server. <*internal|x.y.z.y:1234>
],
"cdr_format": "fwv", // CDR file format <csv|freeswitch_csv|fwv|opensips_flatstore>
"cdr_in_dir": "/tmp/cgr_fwv/cdrc/in", // absolute path towards the directory where the CDRs are stored
"cdr_out_dir": "/tmp/cgr_fwv/cdrc/out", // absolute path towards the directory where processed CDRs will be moved
@@ -60,6 +63,6 @@
{"tag": "TotalDuration", "type": "metatag", "metatag_id":"total_duration", "value": "150", "width": 12},
],
},
},
],
}

View File

@@ -4,7 +4,7 @@
// Used in apier_local_tests
// Starts rater, cdrs and mediator connecting over internal channel
"rater": {
"rals": {
"enabled": true, // enable Rater service: <true|false>
},
@@ -12,8 +12,8 @@
"enabled": true, // start the CDR Server service: <true|false>
"store_cdrs": false, // store cdrs in storDb
"cdr_replication":[ // replicate the rated CDR to a number of servers
{"transport": "*http_post", "server": "http://127.0.0.1:12080/cdr_http", "attempts": 1},
//{"transport": "*http_post", "server": "http://127.0.0.1:8000/mycdr"},
{"transport": "*http_post", "address": "http://127.0.0.1:12080/cdr_http", "attempts": 1},
//{"transport": "*http_post", "address": "http://127.0.0.1:8000/mycdr"},
],
},

View File

@@ -10,7 +10,7 @@
"http": "127.0.0.1:12080", // HTTP listening address
},
"rater": {
"rals": {
"enabled": true, // enable Rater service: <true|false>
},

View File

@@ -5,24 +5,31 @@
// Starts rater, cdrs and mediator connecting over internal channel
"listen": {
"rpc_json": ":2012", // RPC JSON listening address
"rpc_gob": ":2013", // RPC GOB listening address
"http": ":2080", // HTTP listening address
"rpc_json": ":2012",
"rpc_gob": ":2013",
"http": ":2080",
},
"rater": {
"enabled": true, // enable Rater service: <true|false>
"cdrstats": "internal",
"rals": {
"enabled": true,
"cdrstats_conns": [
{"address": "*internal"}
],
},
"cdrs": {
"enabled": true, // start the CDR Server service: <true|false>
"store_cdrs": false, // store cdrs in storDb
"rater": "internal", // address where to reach the Rater for cost calculation, empty to disable functionality: <""|internal|x.y.z.y:1234>
"cdrstats": "internal", // address where to reach the cdrstats service. Empty to disable stats gathering out of mediated CDRs <""|internal|x.y.z.y:1234>
"enabled": true,
"store_cdrs": false,
"rals_conns": [
{"address": "*internal"}
],
"cdrstats_conns": [
{"address": "*internal"}
]
},
"cdrstats": {
"enabled": true, // starts the cdrstats service: <true|false>
"enabled": true,
"save_interval": "1s",
},

View File

@@ -10,13 +10,15 @@
},
"rater": {
"rals": {
"enabled": true, // enable Rater service: <true|false>
},
"cdrs": {
"enabled": true, // start the CDR Server service: <true|false>
"rater": "internal", // address where to reach the Rater for cost calculation, empty to disable functionality: <""|internal|x.y.z.y:1234>
"rals_conns": [
{"address": "*internal"} // address where to reach the Rater for cost calculation, empty to disable functionality: <""|*internal|x.y.z.y:1234>
],
},
}
}

View File

@@ -4,13 +4,15 @@
// Used in apier_local_tests
// Starts rater, cdrs and mediator connecting over internal channel
"rater": {
"rals": {
"enabled": true, // enable Rater service: <true|false>
},
"cdrs": {
"enabled": true, // start the CDR Server service: <true|false>
"rater": "internal", // address where to reach the Rater for cost calculation, empty to disable functionality: <""|internal|x.y.z.y:1234>
"rals_conns": [
{"address": "*internal"} // address where to reach the Rater for cost calculation, empty to disable functionality: <""|*internal|x.y.z.y:1234>
],
},
}
}

View File

@@ -10,13 +10,15 @@
},
"rater": {
"rals": {
"enabled": true, // enable Rater service: <true|false>
},
"cdrs": {
"enabled": true, // start the CDR Server service: <true|false>
"rater": "internal", // address where to reach the Rater for cost calculation, empty to disable functionality: <""|internal|x.y.z.y:1234>
"rals_conns": [
{"address": "*internal"} // address where to reach the Rater for cost calculation, empty to disable functionality: <""|*internal|x.y.z.y:1234>
],
},
}
}

View File

@@ -10,32 +10,38 @@
"http": ":2080", // HTTP listening address
},
//"tariffplan_db": { // database used to store offline tariff plans and CDRs
// "db_type": "mongo", // stor database type to use: <mysql|postgres>
// "db_host": "127.0.0.1", // the host to connect to
// "db_port": 27017, // the port to reach the stordb
// "db_name": "tpdb",
//},
//
//"data_db": { // database used to store offline tariff plans and CDRs
// "db_type": "mongo", // stor database type to use: <mysql|postgres>
// "db_host": "127.0.0.1", // the host to connect to
// "db_port": 27017, // the port to reach the stordb
// "db_name": "datadb",
//},
"tariffplan_db": { // database used to store offline tariff plans and CDRs
"db_type": "mongo", // stor database type to use: <mysql|postgres>
"db_host": "127.0.0.1", // the host to connect to
"db_port": 27017, // the port to reach the stordb
"db_name": "tpdb",
},
"data_db": { // database used to store offline tariff plans and CDRs
"db_type": "mongo", // stor database type to use: <mysql|postgres>
"db_host": "127.0.0.1", // the host to connect to
"db_port": 27017, // the port to reach the stordb
"db_name": "datadb",
},
"stor_db": { // database used to store offline tariff plans and CDRs
"db_type": "mongo", // stor database type to use: <mysql|postgres>
"db_host": "127.0.0.1", // the host to connect to
"db_port": 27017, // the port to reach the stordb
"db_name": "stordb",
"db_name": "stordb",
},
"rater": {
"rals": {
"enabled": true, // enable Rater service: <true|false>
"pubsubs": "internal",
"users": "internal",
"aliases": "internal",
"pubsubs_conns": [
{"address": "*internal"}
],
"users_conns": [
{"address": "*internal"}
],
"aliases_conns": [
{"address": "*internal"}
],
},
"scheduler": {

View File

@@ -10,26 +10,35 @@
"http": ":2080", // HTTP listening address
},
"rater": {
"enabled": true, // enable Rater service: <true|false>
"cdrstats": "internal", // address where to reach the cdrstats service, empty to disable stats functionality<""|internal|x.y.z.y:1234>
"pubsubs": "internal", // address where to reach the pubusb service, empty to disable pubsub functionality: <""|internal|x.y.z.y:1234>
"users": "internal", // address where to reach the user service, empty to disable user profile functionality: <""|internal|x.y.z.y:1234>
"aliases": "internal",
"rals": {
"enabled": true,
"cdrstats_conns": [
{"address": "*internal"}
],
"pubsubs_conns": [
{"address": "*internal"}
],
"users_conns": [
{"address": "*internal"}
],
"aliases_conns": [
{"address": "*internal"}
],
},
"scheduler": {
"enabled": true, // start Scheduler service: <true|false>
"enabled": true,
},
"cdrs": {
"enabled": true, // start the CDR Server service: <true|false>
"rater": "internal", // address where to reach the Rater for cost calculation, empty to disable functionality: <""|internal|x.y.z.y:1234>
"cdrstats": "internal", // address where to reach the cdrstats service, empty to disable stats functionality<""|internal|x.y.z.y:1234>
"enabled": true,
"cdrstats_conns": [
{"address": "*internal"}
],
},
"cdrstats": {
"enabled": true, // starts the cdrstats service: <true|false>
"enabled": true,
},
"pubsubs": {
@@ -47,13 +56,13 @@
"sm_generic": {
"enabled": true,
"rater": "internal",
"cdrs": "internal",
},
"diameter_agent": {
"enabled": true,
"pubsubs": "internal",
"pubsubs_conns": [
{"address": "*internal"}
],
},
}

View File

@@ -0,0 +1,110 @@
{
"diameter_agent": {
"request_processors": [
{
"id": "data_init", // formal identifier of this processor
"dry_run": false, // do not send the events to SMG, just log them
"request_filter": "Service-Context-Id(^gprs);CC-Request-Type(1)", // filter requests processed by this processor
"continue_on_success": false, // continue to the next template if executed
"ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value
{"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*data", "mandatory": true},
{"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Session-Id", "mandatory": true},
{"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "^*prepaid", "mandatory": true},
{"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true},
{"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "^cgrates.org", "mandatory": true},
{"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^generic", "mandatory": true},
{"tag": "Account", "field_id": "Account", "type": "*grouped", "value": "Subscription-Id>Subscription-Id-Data", "field_filter":"Subscription-Id>Subscription-Id-Type(0)", "mandatory": true},
{"tag": "Destination", "field_id": "Destination", "type": "*constant", "value": "data"},
{"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true},
{"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true},
{"tag": "Usage", "field_id": "Usage", "type": "*constant", "value": "2048"},
],
"cca_fields": [
{"tag": "ResultCode", "field_id": "Result-Code", "type": "*constant", "value": "^2001"},
{"tag": "ResultCode", "field_filter": "CGRMaxUsage(0)", "field_id": "Result-Code", "type": "*constant", "value": "4010"},
],
},
{
"id": "data_update_grp1", // formal identifier of this processor
"dry_run": false, // do not send the events to SMG, just log them
"request_filter": "Service-Context-Id(^gprs);CC-Request-Type(2);Multiple-Services-Credit-Control>Rating-Group(1)", // filter requests processed by this processor
"continue_on_success": true, // continue to the next template if executed
"ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value
{"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*data", "mandatory": true},
{"tag": "InitialOriginID", "field_id": "InitialOriginID", "type": "*composed", "value": "Session-Id", "mandatory": true},
{"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Session-Id", "mandatory": true},
{"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "^_grp1", "append": true},
{"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "^*prepaid", "mandatory": true},
{"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true},
{"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "^cgrates.org", "mandatory": true},
{"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^generic", "mandatory": true},
{"tag": "Account", "field_id": "Account", "type": "*grouped", "value": "Subscription-Id>Subscription-Id-Data", "field_filter":"Subscription-Id>Subscription-Id-Type(0)", "mandatory": true},
{"tag": "Destination", "field_id": "Destination", "type": "*constant", "value": "data"},
{"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true},
{"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true},
{"tag": "Usage", "field_id": "Usage", "type": "*constant", "value": "2048"},
{"tag": "LastUsed", "field_id": "LastUsed", "field_filter":"Multiple-Services-Credit-Control>Rating-Group(1)", "type": "*handler", "handler_id": "*sum",
"value": "Multiple-Services-Credit-Control>Used-Service-Unit>CC-Input-Octets;^|;Multiple-Services-Credit-Control>Used-Service-Unit>CC-Output-Octets"},
],
"cca_fields": [
{"tag": "ResultCode", "field_id": "Result-Code", "type": "*constant", "value": "^2001"},
{"tag": "ResultCode", "field_filter": "CGRMaxUsage(0)", "field_id": "Result-Code", "type": "*constant", "value": "4010"},
],
},
{
"id": "data_update_grp2", // formal identifier of this processor
"dry_run": false, // do not send the events to SMG, just log them
"request_filter": "Service-Context-Id(^gprs);CC-Request-Type(2);Multiple-Services-Credit-Control>Rating-Group(2)", // filter requests processed by this processor
"continue_on_success": true, // continue to the next template if executed
"ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value
{"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*data", "mandatory": true},
{"tag": "InitialOriginID", "field_id": "InitialOriginID", "type": "*composed", "value": "Session-Id", "mandatory": true},
{"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Session-Id", "mandatory": true},
{"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "^_grp2", "append": true},
{"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "^*prepaid", "mandatory": true},
{"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true},
{"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "^cgrates.org", "mandatory": true},
{"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^generic", "mandatory": true},
{"tag": "Account", "field_id": "Account", "type": "*grouped", "value": "Subscription-Id>Subscription-Id-Data", "field_filter":"Subscription-Id>Subscription-Id-Type(0)", "mandatory": true},
{"tag": "Destination", "field_id": "Destination", "type": "*constant", "value": "data"},
{"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true},
{"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true},
{"tag": "Usage", "field_id": "Usage", "type": "*constant", "value": "2048"},
{"tag": "LastUsed", "field_id": "LastUsed", "field_filter":"Multiple-Services-Credit-Control>Rating-Group(2)", "type": "*handler", "handler_id": "*sum",
"value": "Multiple-Services-Credit-Control>Used-Service-Unit>CC-Input-Octets;^|;Multiple-Services-Credit-Control>Used-Service-Unit>CC-Output-Octets"},
],
"cca_fields": [
{"tag": "ResultCode", "field_id": "Result-Code", "type": "*constant", "value": "^2001"},
{"tag": "ResultCode", "field_filter": "CGRMaxUsage(0)", "field_id": "Result-Code", "type": "*constant", "value": "4010"},
],
},
{
"id": "data_terminate", // formal identifier of this processor
"dry_run": false, // do not send the events to SMG, just log them
"request_filter": "Service-Context-Id(^gprs);CC-Request-Type(3)", // filter requests processed by this processor
"continue_on_success": false, // continue to the next template if executed
"ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value
{"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*data", "mandatory": true},
{"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Session-Id", "mandatory": true},
{"tag": "OriginIDPrefix", "field_id": "OriginIDPrefix", "type": "*composed", "value": "Session-Id", "mandatory": true},
{"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "^*prepaid", "mandatory": true},
{"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true},
{"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "^cgrates.org", "mandatory": true},
{"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^generic", "mandatory": true},
{"tag": "Account", "field_id": "Account", "type": "*grouped", "value": "Subscription-Id>Subscription-Id-Data", "field_filter":"Subscription-Id>Subscription-Id-Type(0)", "mandatory": true},
{"tag": "Destination", "field_id": "Destination", "type": "*constant", "value": "data"},
{"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true},
{"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true},
{"tag": "LastUsed", "field_id": "LastUsed", "type": "*handler", "handler_id": "*sum",
"value": "Multiple-Services-Credit-Control>Used-Service-Unit>CC-Input-Octets;^|;Multiple-Services-Credit-Control>Used-Service-Unit>CC-Output-Octets"},
],
"cca_fields": [
{"tag": "ResultCode", "field_id": "Result-Code", "type": "*constant", "value": "^2001"}
],
},
]
}
}

View File

@@ -6,9 +6,11 @@
"http": ":2080", // HTTP listening address
},
"rater": {
"rals": {
"enabled": true, // enable Rater service: <true|false>
"cdrstats": "internal", // address where to reach the cdrstats service, empty to disable stats functionality<""|internal|x.y.z.y:1234>
"cdrstats_conns": [
{"address": "*internal"}
],
},
"scheduler": {
@@ -17,13 +19,14 @@
"cdrs": {
"enabled": true, // start the CDR Server service: <true|false>
"rater": "internal", // address where to reach the Rater for cost calculation, empty to disable functionality: <""|internal|x.y.z.y:1234>
"cdrstats": "internal", // address where to reach the cdrstats service, empty to disable stats functionality<""|internal|x.y.z.y:1234>
"cdrstats_conns": [
{"address": "*internal"}
],
},
"cdrstats": {
"enabled": true, // starts the cdrstats service: <true|false>
"save_interval": "5s"
"save_interval": "5s"
},
}

View File

@@ -1,8 +1,9 @@
{
// Contains CDRC template for FreeSWITCH CDR
"cdrc": {
"CDRC-CSV2": {
"cdrc": [
{
"id": "CDRC-CSV2",
"enabled": true, // enable CDR client functionality
"cdr_in_dir": "/tmp/cgrates/cdrc_fs/in", // absolute path towards the directory where the CDRs are stored
"cdr_out_dir": "/tmp/cgrates/cdrc_fs/out", // absolute path towards the directory where processed CDRs will be moved
@@ -22,6 +23,6 @@
{"tag": "usage", "cdr_field_id": "usage", "type": "cdrfield", "value": "~8:s/^(\\d+)$/${1}s/", "mandatory": true},
],
},
},
],
}

View File

@@ -0,0 +1,45 @@
{
"listen": {
"rpc_json": ":2014",
"rpc_gob": ":2015",
"http": ":2081",
},
"rals": {
"enabled": true,
"cdrstats_conns": [
{"address": "*internal"}
],
"pubsubs_conns": [
{"address": "*internal"}
],
"users_conns": [
{"address": "*internal"}
],
"aliases_conns": [
{"address": "*internal"}
],
},
"scheduler": {
"enabled": true,
},
"cdrstats": {
"enabled": true,
},
"pubsubs": {
"enabled": true,
},
"aliases": {
"enabled": true,
},
"users": {
"enabled": true,
"indexes": ["SubscriberId"],
},
}

View File

@@ -0,0 +1,46 @@
{
"listen": {
"rpc_json": ":2016",
"rpc_gob": ":2017",
"http": ":2082",
},
"rals": {
"enabled": true,
"cdrstats_conns": [
{"address": "*internal"}
],
"pubsubs_conns": [
{"address": "*internal"}
],
"users_conns": [
{"address": "*internal"}
],
"aliases_conns": [
{"address": "*internal"}
],
},
"scheduler": {
"enabled": true,
},
"cdrstats": {
"enabled": true,
},
"pubsubs": {
"enabled": true,
},
"aliases": {
"enabled": true,
},
"users": {
"enabled": true,
"indexes": ["SubscriberId"],
},
}

View File

@@ -0,0 +1,27 @@
{
"listen": {
"rpc_json": "127.0.0.1:2018", // RPC JSON listening address
"rpc_gob": "127.0.0.1:2019", // RPC GOB listening address
"http": "127.0.0.1:2083", // HTTP listening address
},
"cdrs": {
"enabled": true, // start the CDR Server service: <true|false>
"rals_conns": [
{"address": "127.0.0.1:2014"},
{"address": "127.0.0.1:2016"}
],
"cdrstats": "", // address where to reach the cdrstats service, empty to disable stats functionality<""|internal|x.y.z.y:1234>
},
"sm_generic": {
"enabled": true,
"rals_conns": [
{"address": "127.0.0.1:2014"},
{"address": "127.0.0.1:2016"}
],
"cdrs_conns": [
{"address": "*internal"} // address where to reach CDR Server, empty to disable CDR capturing <internal|x.y.z.y:1234>
],
},
}

View File

@@ -0,0 +1,28 @@
{
"listen": {
"rpc_json": "127.0.0.1:2020", // RPC JSON listening address
"rpc_gob": "127.0.0.1:2021", // RPC GOB listening address
"http": "127.0.0.1:2084", // HTTP listening address
},
"cdrs": {
"enabled": true, // start the CDR Server service: <true|false>
"rals_conns": [
{"address": "127.0.0.1:2014"},
{"address": "127.0.0.1:2016"}
],
"cdrstats": "", // address where to reach the cdrstats service, empty to disable stats functionality<""|internal|x.y.z.y:1234>
},
"sm_generic": {
"enabled": true,
"rals_conns": [
{"address": "127.0.0.1:2014"},
{"address": "127.0.0.1:2016"}
],
"cdrs_conns": [
{"address": "*internal"} // address where to reach CDR Server, empty to disable CDR capturing <internal|x.y.z.y:1234>
],
},
}

View File

@@ -0,0 +1,10 @@
{
"diameter_agent": {
"enabled": true,
"listen": "127.0.0.1:3868",
"sm_generic_conns": [
{"address": "127.0.0.1:2018"},
{"address": "127.0.0.1:2020"}
],
},
}

View File

@@ -4,7 +4,7 @@
// Should be the second file loaded
"general": {
"default_reqtype": "*pseudoprepaid", // default request type to consider when missing from requests: <""|*prepaid|*postpaid|*pseudoprepaid|*rated>
"default_request_type": "*pseudoprepaid", // default request type to consider when missing from requests: <""|*prepaid|*postpaid|*pseudoprepaid|*rated>
},
"cdre": {

View File

@@ -4,7 +4,7 @@
// Used in mediator_local_test
// Starts rater, cdrs and mediator connecting over internal channel
"rater": {
"rals": {
"enabled": true, // enable Rater service: <true|false>
},
@@ -14,17 +14,18 @@
"cdrs": {
"enabled": true, // start the CDR Server service: <true|false>
"mediator": "internal", // address where to reach the Mediator. Empty for disabling mediation. <""|internal>
},
"cdrc": {
"CDRC-CSV1": {
"cdrc": [
{
"id": "CDRC-CSV1",
"enabled": true, // enable CDR client functionality
"cdr_in_dir": "/tmp/cgrates/cdrc1/in", // absolute path towards the directory where the CDRs are stored
"cdr_out_dir": "/tmp/cgrates/cdrc1/out", // absolute path towards the directory where processed CDRs will be moved
"cdr_source_id": "csv1", // free form field, tag identifying the source of the CDRs within CDRS database
},
"CDRC-CSV2": {
{
"id": "CDRC-CSV2",
"enabled": true, // enable CDR client functionality
"cdr_in_dir": "/tmp/cgrates/cdrc2/in", // absolute path towards the directory where the CDRs are stored
"cdr_out_dir": "/tmp/cgrates/cdrc2/out", // absolute path towards the directory where processed CDRs will be moved
@@ -44,7 +45,8 @@
{"cdr_field_id": "usage", "value": "~9:s/^(\\d+)$/${1}s/"},
],
},
"CDRC-CSV3": {
{
"id": "CDRC-CSV3",
"enabled": true, // enable CDR client functionality
"field_separator": ";", // separator used in case of csv files
"cdr_in_dir": "/tmp/cgrates/cdrc3/in", // absolute path towards the directory where the CDRs are stored
@@ -65,11 +67,7 @@
{"cdr_field_id": "usage", "value": "~6:s/^(\\d+)$/${1}s/"},
],
}
},
"mediator": {
"enabled": true, // starts Mediator service: <true|false>.
},
],
"cdre": {
"CDRE-FW1": {

View File

@@ -0,0 +1,17 @@
{
// CGRateS Configuration file
//
// Used for multiple RAL configuration tests
// Starts rater, scheduler
"listen": {
"rpc_json": ":2012", // RPC JSON listening address
"rpc_gob": ":2013", // RPC GOB listening address
"http": ":2080", // HTTP listening address
},
"rals": {
"enabled": true, // enable Rater service: <true|false>
},
}

View File

@@ -0,0 +1,17 @@
{
// CGRateS Configuration file
//
// Used for multiple RAL configuration tests
// Starts RAL
"listen": {
"rpc_json": ":12012", // RPC JSON listening address
"rpc_gob": ":12013", // RPC GOB listening address
"http": ":12080", // HTTP listening address
},
"rals": {
"enabled": true, // enable Rater service: <true|false>
},
}

View File

@@ -1,61 +0,0 @@
# Real-time Charging System 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.
[global]
rpc_json_listen = :2012 # RPC JSON listening address
[rater]
enabled = true # Enable RaterCDRSExportPath service: <true|false>.
[scheduler]
enabled = true # Starts Scheduler service: <true|false>.
[cdrs]
enabled = true # Start the CDR Server service: <true|false>.
mediator = internal # Address where to reach the Mediator. Empty for disabling mediation. <""|internal>
# cdrstats = # Address where to reach the cdrstats service: <internal|x.y.z.y:1234>
[mediator]
enabled = true # Starts Mediator service: <true|false>.
# rater = internal # Address where to reach the Rater: <internal|x.y.z.y:1234>
# cdrstats = internal # Address where to reach the cdrstats service: <internal|x.y.z.y:1234>
[cdrstats]
enabled = true # Starts the cdrstats service: <true|false>
#queue_length = 50 # Number of items in the stats buffer
time_window = 1h # Will only keep the CDRs who's call setup time is not older than time.Now()-TimeWindow
save_interval = 5s
# metrics = ASR, ACD, ACC # Stat metric ids to build
# setup_interval = # Filter on CDR SetupTime
# tors = # Filter on CDR TOR fields
# cdr_hosts= # Filter on CDR CdrHost fields
# cdr_sources = # Filter on CDR CdrSource fields
# req_types = # Filter on CDR ReqType fields
# directions = # Filter on CDR Direction fields
# tenants = # Filter on CDR Tenant fields
# categories = # Filter on CDR Category fields
# accounts = # Filter on CDR Account fields
# subjects = # Filter on CDR Subject fields
# destination_prefixes = # Filter on CDR Destination prefixes
# usage_interval = # Filter on CDR Usage
# mediation_run_ids = # Filter on CDR MediationRunId fields
# rated_accounts = # Filter on CDR RatedAccount fields
# rated_subjects = # Filter on CDR RatedSubject fields
# cost_intervals = # Filter on CDR Cost
[session_manager]
enabled = true # Starts SessionManager service: <true|false>
switch_type = opensips # Defines the type of switch behind: <freeswitch>
[opensips]
listen_udp = :2020 # Address where to listen for datagram events coming from OpenSIPS
mi_addr = 172.16.254.77:8020 # Adress where to reach OpenSIPS mi_datagram module
[mailer]
# server = localhost # The server to use when sending emails out
# auth_user = cgrates # Authenticate to email server using this user
# auth_passwd = CGRateS.org # Authenticate to email server with this password
# from_address = cgr-mailer@localhost.localdomain # From address used when sending emails out

View File

@@ -0,0 +1,25 @@
{
// Real-time Charging System 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.
"sm_freeswitch": {
"enabled": true,
"rals_conns": [
{"address": "127.0.0.1:2013"}
],
"cdrs_conns": [
{"address": "127.0.0.1:2013"}
],
"debit_interval": "5s",
"channel_sync_interval": "10s",
"event_socket_conns":[
{"address": "127.0.0.1:8021", "password": "ClueCon", "reconnects": 5}
],
},
}

View File

@@ -4,13 +4,17 @@
// Used for cgradmin
// Starts rater, scheduler
"general": {
"response_cache_ttl": "1s",
},
"listen": {
"rpc_json": ":2012", // RPC JSON listening address
"rpc_gob": ":2013", // RPC GOB listening address
"http": ":2080", // HTTP listening address
},
"rater": {
"rals": {
"enabled": true, // enable Rater service: <true|false>
},
@@ -20,13 +24,11 @@
"cdrs": {
"enabled": true, // start the CDR Server service: <true|false>
"rater": "internal", // address where to reach the Rater for cost calculation, empty to disable functionality: <""|internal|x.y.z.y:1234>
},
"sm_generic": {
"enabled": true,
"rater": "internal",
"cdrs": "internal",
"session_ttl": "50ms",
},
}

View File

@@ -6,77 +6,18 @@
// This file contains the default configuration hardcoded into CGRateS.
// This is what you get when you load CGRateS with an empty configuration file.
//"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/log/cgrates/tpe", // path towards export folder for offline Tariff Plans
// "http_failed_dir": "/var/log/cgrates/http_failed", // directory path where we store failed http requests
// "default_reqtype": "*rated", // default request type to consider when missing from requests: <""|*prepaid|*postpaid|*pseudoprepaid|*rated>
// "default_category": "call", // default Type of Record to consider when missing from requests
// "default_tenant": "cgrates.org", // default Tenant to consider when missing from requests
// "default_subject": "cgrates", // default rating Subject 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
// "response_cache_ttl": "3s", // the life span of a cached response
// "internal_ttl": "2m", // maximum duration to wait for internal connections before giving up
//},
//"listen": {
// "rpc_json": "127.0.0.1:2012", // RPC JSON listening address
// "rpc_gob": "127.0.0.1:2013", // RPC GOB listening address
// "http": "127.0.0.1:2080", // HTTP listening address
//},
//"tariffplan_db": { // database used to store active tariff plan configuration
// "db_type": "redis", // tariffplan_db type: <redis>
// "db_host": "127.0.0.1", // tariffplan_db host address
// "db_port": 6379, // port to reach the tariffplan_db
// "db_name": "10", // tariffplan_db name to connect to
// "db_user": "", // sername to use when connecting to tariffplan_db
// "db_passwd": "", // password to use when connecting to tariffplan_db
//},
//"data_db": { // database used to store runtime data (eg: accounts, cdr stats)
// "db_type": "redis", // data_db type: <redis>
// "db_host": "127.0.0.1", // data_db host address
// "db_port": 6379, // data_db port to reach the database
// "db_name": "11", // data_db database name to connect to
// "db_user": "", // username to use when connecting to data_db
// "db_passwd": "", // password to use when connecting to data_db
// "load_history_size": 10, // Number of records in the load history
//},
//"stor_db": { // database used to store offline tariff plans and CDRs
// "db_type": "mysql", // stor database type to use: <mysql|postgres>
// "db_host": "127.0.0.1", // the host to connect to
// "db_port": 3306, // the port to reach the stordb
// "db_name": "cgrates", // stor database name
// "db_user": "cgrates", // username to use when connecting to stordb
// "db_passwd": "CGRateS.org", // password to use when connecting to stordb
// "max_open_conns": 100, // maximum database connections opened
// "max_idle_conns": 10, // maximum database connections idle
//},
//"balancer": {
// "enabled": false, // start Balancer service: <true|false>
//},
"rater": {
"rals": {
"enabled": true, // enable Rater service: <true|false>
// "balancer": "", // register to balancer as worker: <""|internal|x.y.z.y:1234>
"cdrstats": "internal", // address where to reach the cdrstats service, empty to disable stats functionality: <""|internal|x.y.z.y:1234>
// "historys": "", // address where to reach the history service, empty to disable history functionality: <""|internal|x.y.z.y:1234>
// "pubsubs": "", // address where to reach the pubusb service, empty to disable pubsub functionality: <""|internal|x.y.z.y:1234>
"users": "internal", // address where to reach the user service, empty to disable user profile functionality: <""|internal|x.y.z.y:1234>
"aliases": "internal", // address where to reach the aliases service, empty to disable aliases functionality: <""|internal|x.y.z.y:1234>
"cdrstats_conns": [
{"address": "*internal"}
],
"users_conns": [
{"address": "*internal"}
],
"aliases_conns": [
{"address": "*internal"}
],
},
@@ -87,168 +28,19 @@
"cdrs": {
"enabled": true, // start the CDR Server service: <true|false>
// "extra_fields": [], // extra fields to store in CDRs for non-generic CDRs
// "store_cdrs": true, // store cdrs in storDb
// "rater": "internal", // address where to reach the Rater for cost calculation, empty to disable functionality: <""|internal|x.y.z.y:1234>
// "pubsubs": "", // address where to reach the pubusb service, empty to disable pubsub functionality: <""|internal|x.y.z.y:1234>
// "users": "", // address where to reach the user service, empty to disable user profile functionality: <""|internal|x.y.z.y:1234>
// "aliases": "", // address where to reach the aliases service, empty to disable aliases functionality: <""|internal|x.y.z.y:1234>
// "cdrstats": "", // address where to reach the cdrstats service, empty to disable stats functionality<""|internal|x.y.z.y:1234>
// "cdr_replication":[], // replicate the raw CDR to a number of servers
},
"cdrstats": {
"enabled": true, // starts the cdrstats service: <true|false>
// "save_interval": "1m", // interval to save changed stats into dataDb storage
},
//"cdre": {
// "*default": {
// "cdr_format": "csv", // exported CDRs format <csv>
// "field_separator": ",",
// "data_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from KBytes to Bytes)
// "sms_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from SMS unit to call duration in some billing systems)
// "generic_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from GENERIC unit to call duration in some billing systems)
// "cost_multiply_factor": 1, // multiply cost before export, eg: add VAT
// "cost_rounding_decimals": -1, // rounding decimals for Cost values. -1 to disable rounding
// "cost_shift_digits": 0, // shift digits in the cost on export (eg: convert from EUR to cents)
// "mask_destination_id": "MASKED_DESTINATIONS", // destination id containing called addresses to be masked on export
// "mask_length": 0, // length of the destination suffix to be masked
// "export_dir": "/var/log/cgrates/cdre", // path where the exported CDRs will be placed
// "header_fields": [], // template of the exported header fields
// "content_fields": [ // template of the exported content fields
// {"tag": "CgrId", "cdr_field_id": "CgrId", "type": "cdrfield", "value": "CgrId"},
// {"tag":"RunId", "cdr_field_id": "MediationRunId", "type": "cdrfield", "value": "MediationRunId"},
// {"tag":"Tor", "cdr_field_id": "TOR", "type": "cdrfield", "value": "TOR"},
// {"tag":"AccId", "cdr_field_id": "AccId", "type": "cdrfield", "value": "AccId"},
// {"tag":"ReqType", "cdr_field_id": "ReqType", "type": "cdrfield", "value": "ReqType"},
// {"tag":"Direction", "cdr_field_id": "Direction", "type": "cdrfield", "value": "Direction"},
// {"tag":"Tenant", "cdr_field_id": "Tenant", "type": "cdrfield", "value": "Tenant"},
// {"tag":"Category", "cdr_field_id": "Category", "type": "cdrfield", "value": "Category"},
// {"tag":"Account", "cdr_field_id": "Account", "type": "cdrfield", "value": "Account"},
// {"tag":"Subject", "cdr_field_id": "Subject", "type": "cdrfield", "value": "Subject"},
// {"tag":"Destination", "cdr_field_id": "Destination", "type": "cdrfield", "value": "Destination"},
// {"tag":"SetupTime", "cdr_field_id": "SetupTime", "type": "cdrfield", "value": "SetupTime", "layout": "2006-01-02T15:04:05Z07:00"},
// {"tag":"AnswerTime", "cdr_field_id": "AnswerTime", "type": "cdrfield", "value": "AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"},
// {"tag":"Usage", "cdr_field_id": "Usage", "type": "cdrfield", "value": "Usage"},
// {"tag":"Cost", "cdr_field_id": "Cost", "type": "cdrfield", "value": "Cost"},
// ],
// "trailer_fields": [], // template of the exported trailer fields
// }
//},
//"cdrc": {
// "*default": {
// "enabled": false, // enable CDR client functionality
// "dry_run": false, // do not send the CDRs to CDRS, just parse them
// "cdrs": "internal", // address where to reach CDR server. <internal|x.y.z.y:1234>
// "cdr_format": "csv", // CDR file format <csv|freeswitch_csv|fwv|opensips_flatstore>
// "field_separator": ",", // separator used in case of csv files
// "timezone": "", // timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB>
// "run_delay": 0, // sleep interval in seconds between consecutive runs, 0 to use automation via inotify
// "max_open_files": 1024, // maximum simultaneous files to process, 0 for unlimited
// "data_usage_multiply_factor": 1024, // conversion factor for data usage
// "cdr_in_dir": "/var/log/cgrates/cdrc/in", // absolute path towards the directory where the CDRs are stored
// "cdr_out_dir": "/var/log/cgrates/cdrc/out", // absolute path towards the directory where processed CDRs will be moved
// "failed_calls_prefix": "missed_calls", // used in case of flatstore CDRs to avoid searching for BYE records
// "cdr_source_id": "freeswitch_csv", // free form field, tag identifying the source of the CDRs within CDRS database
// "cdr_filter": "", // filter CDR records to import
// "partial_record_cache": "10s", // duration to cache partial records when not pairing
// "header_fields": [], // template of the import header fields
// "content_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value
// {"tag": "tor", "cdr_field_id": "TOR", "type": "cdrfield", "value": "2", "mandatory": true},
// {"tag": "accid", "cdr_field_id": "AccId", "type": "cdrfield", "value": "3", "mandatory": true},
// {"tag": "reqtype", "cdr_field_id": "ReqType", "type": "cdrfield", "value": "4", "mandatory": true},
// {"tag": "direction", "cdr_field_id": "Direction", "type": "cdrfield", "value": "5", "mandatory": true},
// {"tag": "tenant", "cdr_field_id": "Tenant", "type": "cdrfield", "value": "6", "mandatory": true},
// {"tag": "category", "cdr_field_id": "Category", "type": "cdrfield", "value": "7", "mandatory": true},
// {"tag": "account", "cdr_field_id": "Account", "type": "cdrfield", "value": "8", "mandatory": true},
// {"tag": "subject", "cdr_field_id": "Subject", "type": "cdrfield", "value": "9", "mandatory": true},
// {"tag": "destination", "cdr_field_id": "Destination", "type": "cdrfield", "value": "10", "mandatory": true},
// {"tag": "setup_time", "cdr_field_id": "SetupTime", "type": "cdrfield", "value": "11", "mandatory": true},
// {"tag": "answer_time", "cdr_field_id": "AnswerTime", "type": "cdrfield", "value": "12", "mandatory": true},
// {"tag": "usage", "cdr_field_id": "Usage", "type": "cdrfield", "value": "13", "mandatory": true},
// ],
// "trailer_fields": [], // template of the import trailer fields
// }
//},
"sm_generic": {
"enabled": true, // starts SessionManager service: <true|false>
// "listen_bijson": "127.0.0.1:2014", // address where to listen for bidirectional JSON-RPC requests
// "rater": "internal", // address where to reach the Rater <""|internal|127.0.0.1:2013>
// "cdrs": "internal", // address where to reach CDR Server <""|internal|x.y.z.y:1234>
// "debit_interval": "10s", // interval to perform debits on.
// "min_call_duration": "0s", // only authorize calls with allowed duration higher than this
// "max_call_duration": "3h", // maximum call duration a prepaid call can last
},
//"sm_freeswitch": {
// "enabled": false, // starts SessionManager service: <true|false>
// "rater": "internal", // address where to reach the Rater <""|internal|127.0.0.1:2013>
// "cdrs": "internal", // address where to reach CDR Server, empty to disable CDR capturing <""|internal|x.y.z.y:1234>
// "create_cdr": false, // create CDR out of events and sends them to CDRS component
// "extra_fields": [], // extra fields to store in auth/CDRs when creating them
// "debit_interval": "10s", // interval to perform debits on.
// "min_call_duration": "0s", // only authorize calls with allowed duration higher than this
// "max_call_duration": "3h", // maximum call duration a prepaid call can last
// "min_dur_low_balance": "5s", // threshold which will trigger low balance warnings for prepaid calls (needs to be lower than debit_interval)
// "low_balance_ann_file": "", // file to be played when low balance is reached for prepaid calls
// "empty_balance_context": "", // if defined, prepaid calls will be transfered to this context on empty balance
// "empty_balance_ann_file": "", // file to be played before disconnecting prepaid calls on empty balance (applies only if no context defined)
// "subscribe_park": true, // subscribe via fsock to receive park events
// "channel_sync_interval": "5m", // sync channels with freeswitch regularly
// "connections":[ // instantiate connections to multiple FreeSWITCH servers
// {"server": "127.0.0.1:8021", "password": "ClueCon", "reconnects": 5}
// ],
//},
//"sm_kamailio": {
// "enabled": false, // starts SessionManager service: <true|false>
// "rater": "internal", // address where to reach the Rater <""|internal|127.0.0.1:2013>
// "cdrs": "internal", // address where to reach CDR Server, empty to disable CDR capturing <""|internal|x.y.z.y:1234>
// "create_cdr": false, // create CDR out of events and sends them to CDRS component
// "debit_interval": "10s", // interval to perform debits on.
// "min_call_duration": "0s", // only authorize calls with allowed duration higher than this
// "max_call_duration": "3h", // maximum call duration a prepaid call can last
// "connections":[ // instantiate connections to multiple Kamailio servers
// {"evapi_addr": "127.0.0.1:8448", "reconnects": 5}
// ],
//},
//"sm_opensips": {
// "enabled": false, // starts SessionManager service: <true|false>
// "listen_udp": "127.0.0.1:2020", // address where to listen for datagram events coming from OpenSIPS
// "rater": "internal", // address where to reach the Rater <""|internal|127.0.0.1:2013>
// "cdrs": "internal", // address where to reach CDR Server, empty to disable CDR capturing <""|internal|x.y.z.y:1234>
// "reconnects": 5, // number of reconnects if connection is lost
// "create_cdr": false, // create CDR out of events and sends them to CDRS component
// "debit_interval": "10s", // interval to perform debits on.
// "min_call_duration": "0s", // only authorize calls with allowed duration higher than this
// "max_call_duration": "3h", // maximum call duration a prepaid call can last
// "events_subscribe_interval": "60s", // automatic events subscription to OpenSIPS, 0 to disable it
// "mi_addr": "127.0.0.1:8020", // address where to reach OpenSIPS MI to send session disconnects
//},
//"historys": {
// "enabled": false, // starts History service: <true|false>.
// "history_dir": "/var/log/cgrates/history", // location on disk where to store history files.
// "save_interval": "1s", // interval to save changed cache into .git archive
//},
//"pubsubs": {
// "enabled": false, // starts PubSub service: <true|false>.
//},
"aliases": {
"enabled": true, // starts Aliases service: <true|false>.
},
@@ -256,45 +48,7 @@
"users": {
"enabled": true, // starts User service: <true|false>.
// "indexes": [], // user profile field indexes
},
//"mailer": {
// "server": "localhost", // the server to use when sending emails out
// "auth_user": "cgrates", // authenticate to email server using this user
// "auth_passwd": "CGRateS.org", // authenticate to email server with this password
// "from_address": "cgr-mailer@localhost.localdomain" // from address used when sending emails out
//},
//"suretax": {
// "url": "", // API url
// "client_number": "", // client number, provided by SureTax
// "validation_key": "", // validation key provided by SureTax
// "business_unit": "", // clients Business Unit
// "timezone": "Local", // convert the time of the events to this timezone before sending request out <UTC|Local|$IANA_TZ_DB>
// "include_local_cost": false, // sum local calculated cost with tax one in final cost
// "return_file_code": "0", // default or Quote purposes <0|Q>
// "response_group": "03", // determines how taxes are grouped for the response <03|13>
// "response_type": "D4", // determines the granularity of taxes and (optionally) the decimal precision for the tax calculations and amounts in the response
// "regulatory_code": "03", // provider type
// "client_tracking": "CgrId", // template extracting client information out of StoredCdr; <$RSRFields>
// "customer_number": "Subject", // template extracting customer number out of StoredCdr; <$RSRFields>
// "orig_number": "Subject", // template extracting origination number out of StoredCdr; <$RSRFields>
// "term_number": "Destination", // template extracting termination number out of StoredCdr; <$RSRFields>
// "bill_to_number": "", // template extracting billed to number out of StoredCdr; <$RSRFields>
// "zipcode": "", // template extracting billing zip code out of StoredCdr; <$RSRFields>
// "plus4": "", // template extracting billing zip code extension out of StoredCdr; <$RSRFields>
// "p2pzipcode": "", // template extracting secondary zip code out of StoredCdr; <$RSRFields>
// "p2pplus4": "", // template extracting secondary zip code extension out of StoredCdr; <$RSRFields>
// "units": "^1", // template extracting number of “lines” or unique charges contained within the revenue out of StoredCdr; <$RSRFields>
// "unit_type": "^00", // template extracting number of unique access lines out of StoredCdr; <$RSRFields>
// "tax_included": "^0", // template extracting tax included in revenue out of StoredCdr; <$RSRFields>
// "tax_situs_rule": "^04", // template extracting tax situs rule out of StoredCdr; <$RSRFields>
// "trans_type_code": "^010101", // template extracting transaction type indicator out of StoredCdr; <$RSRFields>
// "sales_type_code": "^R", // template extracting sales type code out of StoredCdr; <$RSRFields>
// "tax_exemption_code_list": "", // template extracting tax exemption code list out of StoredCdr; <$RSRFields>
//},
}
}

View File

@@ -10,11 +10,17 @@
"http": ":2080", // HTTP listening address
},
"rater": {
"rals": {
"enabled": true, // enable Rater service: <true|false>
"cdrstats": "internal", // address where to reach the cdrstats service, empty to disable stats functionality<""|internal|x.y.z.y:1234>
"pubsubs": "internal", // address where to reach the pubusb service, empty to disable pubsub functionality: <""|internal|x.y.z.y:1234>
"users": "internal", // address where to reach the user service, empty to disable user profile functionality: <""|internal|x.y.z.y:1234>
"cdrstats_conns": [
{"address": "*internal"}
],
"pubsubs_conns": [
{"address": "*internal"}
],
"users_conns": [
{"address": "*internal"}
],
},
"scheduler": {
@@ -23,8 +29,9 @@
"cdrs": {
"enabled": true, // start the CDR Server service: <true|false>
"rater": "internal", // address where to reach the Rater for cost calculation, empty to disable functionality: <""|internal|x.y.z.y:1234>
"cdrstats": "internal", // address where to reach the cdrstats service, empty to disable stats functionality<""|internal|x.y.z.y:1234>
"cdrstats_conns": [
{"address": "*internal"}
],
},
"cdrstats": {

View File

@@ -36,6 +36,15 @@
<rule avp="Recipient-Address" required="false" max="1" />
</data>
</avp>
<avp name="TransactionId" code="29001" must="V" may="P,M" must-not="-" may-encrypt="N" vendor-id="2011">
<data type="OctetString"/>
</avp>
<avp name="MC-Service-Id" code="29002" must="V" may="P,M" must-not="-" may-encrypt="N" vendor-id="2011">
<data type="UTF8String"/>
</avp>
<avp name="TransparentData" code="29003" must="V" may="P,M" must-not="-" may-encrypt="N" vendor-id="2011">
<data type="UTF8String"/>
</avp>
<avp name="CallingPartyAddress" code="20336" must="V" may="P,M" must-not="-" may-encrypt="N" vendor-id="2011">
<data type="UTF8String" />
</avp>
@@ -133,7 +142,7 @@
<avp name="Re-Routing-Number" code="20937" must="V" may="P,M" must-not="-" may-encrypt="N" vendor-id="2011">
<data type="UTF8String" />
</avp>
<avp name="HighLayerCharacteristics" code="29001" must="V" may="P,M" must-not="-" may-encrypt="N" vendor-id="2011">
<avp name="HighLayerCharacteristics" code="20938" must="V" may="P,M" must-not="-" may-encrypt="N" vendor-id="2011">
<data type="OctetString" />
</avp>
<avp name="MC-Service-Id" code="29002" must="V" may="P,M" must-not="-" may-encrypt="N" vendor-id="2011">
@@ -143,4 +152,4 @@
<data type="UTF8String" />
</avp>
</application>
</diameter>
</diameter>

View File

@@ -43,8 +43,8 @@
<avp name="Time-Of-First-Usage" code="263" must="M,V" may="-" must-not="-" may-encrypt="Y" vendor-id="12645">
<data type="Time" />
</avp>
<avp name="Time-Of-Last-Usage" code="266" must="M,V" may="-" must-not="-" may-encrypt="Y" vendor-id="12645">
<avp name="Time-Of-Last-Usage" code="264" must="M,V" may="-" must-not="-" may-encrypt="Y" vendor-id="12645">
<data type="Time" />
</avp>
</application>
</diameter>
</diameter>

View File

@@ -17,7 +17,7 @@ RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927
RUN echo 'deb http://repo.mongodb.org/apt/debian wheezy/mongodb-org/3.2 main' | tee '/etc/apt/sources.list.d/mongodb-org-3.2.list'
# install dependencies
RUN apt-get -y update && apt-get -y install git bzr mercurial redis-server mysql-server python-pycurl python-mysqldb postgresql postgresql-client sudo wget freeswitch-meta-vanilla vim zsh mongodb-org
RUN apt-get -y update && apt-get -y install git bzr mercurial redis-server mysql-server python-pycurl python-mysqldb postgresql postgresql-client sudo wget freeswitch-meta-vanilla vim zsh mongodb-org tmux rsyslog ngrep
# add mongo conf
COPY mongod.conf /etc/mongod.conf
@@ -26,7 +26,7 @@ COPY mongod.conf /etc/mongod.conf
RUN useradd -c CGRateS -d /var/run/cgrates -s /bin/false -r cgrates
# install golang
RUN wget -qO- https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz | tar xzf - -C /root/
RUN wget -qO- https://storage.googleapis.com/golang/go1.6.2.linux-amd64.tar.gz | tar xzf - -C /root/
#install glide
RUN GOROOT=/root/go GOPATH=/root/code /root/go/bin/go get github.com/Masterminds/glide
@@ -34,6 +34,9 @@ RUN GOROOT=/root/go GOPATH=/root/code /root/go/bin/go get github.com/Masterminds
#install oh-my-zsh
RUN TERM=xterm sh -c "$(wget https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)"; exit 0
# change shell for tmux
RUN chsh -s /usr/bin/zsh
# cleanup
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

Some files were not shown because too many files have changed in this diff Show More