Diameter integration tests infrastructure added back

This commit is contained in:
DanB
2018-10-01 20:34:51 +02:00
parent 749d277959
commit 1c079a935b
14 changed files with 205 additions and 111 deletions

186
agents/diam_it_test.go Normal file
View File

@@ -0,0 +1,186 @@
// +build integration
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package agents
import (
"flag"
"net/rpc"
"net/rpc/jsonrpc"
"path"
"testing"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
"github.com/fiorix/go-diameter/diam"
"github.com/fiorix/go-diameter/diam/avp"
"github.com/fiorix/go-diameter/diam/datatype"
"github.com/fiorix/go-diameter/diam/dict"
)
var waitRater = flag.Int("wait_rater", 100, "Number of miliseconds to wait for rater to start and cache")
var dataDir = flag.String("data_dir", "/usr/share/cgrates", "CGR data dir path here")
var interations = flag.Int("iterations", 1, "Number of iterations to do for dry run simulation")
var replyTimeout = flag.String("reply_timeout", "1s", "Maximum duration to wait for a reply")
var daCfgPath string
var daCfg *config.CGRConfig
var apierRpc *rpc.Client
var dmtClient *DiameterClient
var rplyTimeout time.Duration
func TestDiamITInitCfg(t *testing.T) {
daCfgPath = path.Join(*dataDir, "conf", "samples", "diamagent")
// 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)
rplyTimeout, _ = utils.ParseDurationWithSecs(*replyTimeout)
}
// Remove data in both rating and accounting db
func TestDiamITResetDataDb(t *testing.T) {
if err := engine.InitDataDb(daCfg); err != nil {
t.Fatal(err)
}
}
// Wipe out the cdr database
func TestDiamITResetStorDb(t *testing.T) {
if err := engine.InitStorDb(daCfg); err != nil {
t.Fatal(err)
}
}
/*
// Start CGR Engine
func TestDiamITStartEngine(t *testing.T) {
if _, err := engine.StopStartEngine(daCfgPath, 4000); err != nil {
t.Fatal(err)
}
}
*/
func TestDiamITConnectDiameterClient(t *testing.T) {
dmtClient, err = NewDiameterClient(daCfg.DiameterAgentCfg().Listen, "INTEGRATION_TESTS",
daCfg.DiameterAgentCfg().OriginRealm,
daCfg.DiameterAgentCfg().VendorId, daCfg.DiameterAgentCfg().ProductName,
utils.DIAMETER_FIRMWARE_REVISION, daCfg.DiameterAgentCfg().DictionariesPath)
if err != nil {
t.Fatal(err)
}
}
// Connect rpc client to rater
func TestDiamITApierRpcConn(t *testing.T) {
var err error
apierRpc, err = jsonrpc.Dial("tcp", daCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
if err != nil {
t.Fatal(err)
}
}
// Load the tariff plan, creating accounts and their balances
func TestDiamITTPFromFolder(t *testing.T) {
attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "oldtutorial")}
var loadInst utils.LoadInstance
if err := apierRpc.Call("ApierV2.LoadTariffPlanFromFolder", attrs, &loadInst); err != nil {
t.Error(err)
}
time.Sleep(time.Duration(1000) * time.Millisecond) // Give time for scheduler to execute topups
}
func TestDiamITDryRun(t *testing.T) {
ccr := diam.NewRequest(diam.CreditControl, 4, nil)
ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("cgrates;1451911932;00082"))
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.DestinationRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org"))
ccr.NewAVP(avp.DestinationHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA"))
ccr.NewAVP(avp.UserName, avp.Mbit, 0, datatype.UTF8String("CGR-DA"))
ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4))
ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("TestDiamITDryRun")) // Match specific DryRun profile
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.TerminationCause, avp.Mbit, 0, datatype.Enumerated(1))
ccr.NewAVP(443, avp.Mbit, 0, &diam.GroupedAVP{ // Subscription-Id
AVP: []*diam.AVP{
diam.NewAVP(450, avp.Mbit, 0, datatype.Enumerated(0)), // Subscription-Id-Type
diam.NewAVP(444, avp.Mbit, 0, datatype.UTF8String("1001")), // Subscription-Id-Data
}})
ccr.NewAVP(443, avp.Mbit, 0, &diam.GroupedAVP{ // Subscription-Id
AVP: []*diam.AVP{
diam.NewAVP(450, avp.Mbit, 0, datatype.Enumerated(1)), // Subscription-Id-Type
diam.NewAVP(444, avp.Mbit, 0, datatype.UTF8String("208123456789")), // Subscription-Id-Data
}})
ccr.NewAVP(439, avp.Mbit, 0, datatype.Unsigned32(0)) // Service-Identifier
ccr.NewAVP(437, avp.Mbit, 0, &diam.GroupedAVP{ // Requested-Service-Unit
AVP: []*diam.AVP{
diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(300)), // CC-Time
}})
ccr.NewAVP(873, avp.Mbit|avp.Vbit, 10415, &diam.GroupedAVP{ // Service-information
AVP: []*diam.AVP{
diam.NewAVP(20300, avp.Mbit, 2011, &diam.GroupedAVP{ // IN-Information
AVP: []*diam.AVP{
diam.NewAVP(20336, avp.Mbit, 2011, datatype.UTF8String("1001")), // CallingPartyAdress
diam.NewAVP(20337, avp.Mbit, 2011, datatype.UTF8String("1002")), // CalledPartyAdress
diam.NewAVP(20339, avp.Mbit, 2011, datatype.Unsigned32(0)), // ChargeFlowType
diam.NewAVP(20302, avp.Mbit, 2011, datatype.UTF8String("33609004940")), // CallingVlrNumber
diam.NewAVP(20303, avp.Mbit, 2011, datatype.UTF8String("208104941749984")), // CallingCellID
diam.NewAVP(20313, avp.Mbit, 2011, datatype.OctetString("0x8090a3")), // BearerCapability
diam.NewAVP(20321, avp.Mbit, 2011, datatype.OctetString("0x401c4132ed665")), // CallreferenceNumber
diam.NewAVP(20322, avp.Mbit, 2011, datatype.UTF8String("33609004940")), // MSCAddress
diam.NewAVP(20386, avp.Mbit, 2011, datatype.UTF8String("20160501010101")), // SSPTime
diam.NewAVP(20938, avp.Mbit, 2011, datatype.OctetString("0x00000001")), // HighLayerCharacteristics
diam.NewAVP(20324, avp.Mbit, 2011, datatype.Integer32(8)), // Time-Zone
},
}),
}})
if _, err := ccr.NewAVP("Framed-IP-Address", avp.Mbit, 0, datatype.UTF8String("10.228.16.4")); err != nil {
t.Error(err)
}
for i := 0; i < *interations; i++ {
if err := dmtClient.SendMessage(ccr); err != nil {
t.Error(err)
}
msg := dmtClient.ReceivedMessage(rplyTimeout)
if msg == nil {
t.Fatal("No message returned")
}
avps, err := msg.FindAVPsWithPath([]interface{}{"Result-Code"}, dict.UndefinedVendorID)
if err != nil {
t.Error(err)
}
if len(avps) == 0 {
t.Error("Result-Code")
}
eVal := "300"
if val, err := diamAVPAsString(avps[0]); err != nil {
t.Error(err)
} else if val != eVal {
t.Errorf("expecting: %s, received: <%s>", eVal, val)
}
}
}

View File

@@ -133,13 +133,13 @@ func (da *DiameterAgent) handleMessage(c diam.Conn, m *diam.Message) {
utils.Logger.Warning(
fmt.Sprintf("<%s> error: %s processing message: %s",
utils.DiameterAgent, err.Error(), m))
writeOnConn(c, m.Answer(diam.UnableToDeliver))
writeOnConn(c, m.Answer(diam.UnableToComply))
return
} else if !processed {
utils.Logger.Warning(
fmt.Sprintf("<%s> no request processor enabled, ignoring message %s from %s",
utils.DiameterAgent, m, c.RemoteAddr()))
writeOnConn(c, m.Answer(diam.UnableToDeliver))
writeOnConn(c, m.Answer(diam.UnableToComply))
return
}
a := m.Answer(diam.Success)
@@ -150,7 +150,7 @@ func (da *DiameterAgent) handleMessage(c diam.Conn, m *diam.Message) {
utils.Logger.Warning(
fmt.Sprintf("<%s> cannot encode reply field: %s, ignoring message %s from %s",
utils.DiameterAgent, utils.ToJSON(val), m, c.RemoteAddr()))
writeOnConn(c, m.Answer(diam.UnableToDeliver))
writeOnConn(c, m.Answer(diam.UnableToComply))
return
}
// find out the first itm which is not an attribute
@@ -170,7 +170,7 @@ func (da *DiameterAgent) handleMessage(c diam.Conn, m *diam.Message) {
utils.Logger.Warning(
fmt.Sprintf("<%s> error: %s processing reply item: %s for message: %s",
utils.DiameterAgent, err.Error(), utils.ToJSON(itm), m))
writeOnConn(c, m.Answer(diam.UnableToDeliver))
writeOnConn(c, m.Answer(diam.UnableToComply))
return
}
var apnd bool
@@ -182,11 +182,11 @@ func (da *DiameterAgent) handleMessage(c diam.Conn, m *diam.Message) {
utils.Logger.Warning(
fmt.Sprintf("<%s> error: %s setting reply item: %s for message: %s",
utils.DiameterAgent, err.Error(), utils.ToJSON(itm), m))
writeOnConn(c, m.Answer(diam.UnableToDeliver))
writeOnConn(c, m.Answer(diam.UnableToComply))
return
}
}
writeOnConn(c, m.Answer(diam.UnableToDeliver))
writeOnConn(c, m.Answer(diam.UnableToComply))
}
func (da *DiameterAgent) processRequest(reqProcessor *config.DARequestProcessor,
@@ -216,7 +216,7 @@ func (da *DiameterAgent) processRequest(reqProcessor *config.DARequestProcessor,
case utils.MetaDryRun:
utils.Logger.Info(
fmt.Sprintf("<%s> DRY_RUN, processorID: %s, CGREvent: %s",
utils.DiameterAgent, reqProcessor.Id, utils.ToJSON(cgrEv)))
utils.DiameterAgent, reqProcessor.ID, utils.ToJSON(cgrEv)))
case utils.MetaAuth:
authArgs := sessions.NewV1AuthorizeArgs(
reqProcessor.Flags.HasKey(utils.MetaAttributes),

View File

@@ -21,7 +21,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package agents
import (
"flag"
"fmt"
"io/ioutil"
"net/http"
@@ -43,8 +42,6 @@ var (
haRPC *rpc.Client
httpC *http.Client // so we can cache the connection
err error
dataDir = flag.String("data_dir", "/usr/share/cgrates", "CGR data dir path here")
waitRater = flag.Int("wait_rater", 100, "Number of miliseconds to wait for rater to start and cache")
)
func TestHAitInitCfg(t *testing.T) {

View File

@@ -110,6 +110,14 @@ func diamAVPAsIface(dAVP *diam.AVP) (val interface{}, err error) {
}
}
func diamAVPAsString(dAVP *diam.AVP) (s string, err error) {
var iface interface{}
if iface, err = diamAVPAsIface(dAVP); err != nil {
return
}
return utils.IfaceAsString(iface)
}
// newDiamDataType constructs dataType from valStr
func newDiamDataType(typ datatype.TypeID, valStr,
tmz string) (dt datatype.Type, err error) {

View File

@@ -82,7 +82,7 @@ func (da *DiameterAgentCfg) loadFromJsonCfg(jsnCfg *DiameterAgentJsonCfg) (err e
rp := new(DARequestProcessor)
var haveID bool
for _, rpSet := range da.RequestProcessors {
if reqProcJsn.Id != nil && rpSet.Id == *reqProcJsn.Id {
if reqProcJsn.Id != nil && rpSet.ID == *reqProcJsn.Id {
rp = rpSet // Will load data into the one set
haveID = true
break
@@ -101,7 +101,7 @@ func (da *DiameterAgentCfg) loadFromJsonCfg(jsnCfg *DiameterAgentJsonCfg) (err e
// One Diameter request processor configuration
type DARequestProcessor struct {
Id string
ID string
Tenant RSRParsers
Filters []string
Flags utils.StringMap
@@ -116,7 +116,7 @@ func (dap *DARequestProcessor) loadFromJsonCfg(jsnCfg *DARequestProcessorJsnCfg)
return nil
}
if jsnCfg.Id != nil {
dap.Id = *jsnCfg.Id
dap.ID = *jsnCfg.Id
}
if jsnCfg.Tenant != nil {
dap.Tenant = NewRSRParsersMustCompile(*jsnCfg.Tenant, true)

View File

@@ -38,9 +38,6 @@
"cdrs": {
"enabled": true,
"cdrstats_conns": [
{"address": "*internal"}
],
},
"attributes": {

View File

@@ -3,100 +3,6 @@
<application id="4" type="auth">
<!-- Diameter Credit Control Application -->
<!-- http://tools.ietf.org/html/rfc4006 -->
<command code="272" short="CC" name="Credit-Control">
<request>
<!-- http://tools.ietf.org/html/rfc4006#section-3.1 -->
<rule avp="Session-Id" required="true" max="1" />
<rule avp="Origin-Host" required="true" max="1" />
<rule avp="Origin-Realm" required="true" max="1" />
<rule avp="Destination-Host" required="false" max="1" />
<rule avp="Destination-Realm" required="true" max="1" />
<rule avp="Auth-Application-Id" required="true" max="1" />
<rule avp="Service-Context-Id" required="true" max="1" />
<rule avp="CC-Request-Type" required="true" max="1" />
<rule avp="CC-Request-Number" required="true" max="1" />
<rule avp="Event-Timestamp" required="false" max="1" />
<rule avp="Subscription-Id" required="false" max="2" />
<rule avp="Service-Identifier" required="false" max="1" />
<rule avp="Termination-Cause" required="false" max="1" />
<rule avp="Requested-Service-Unit" required="false" max="1" />
<rule avp="Used-Service-Unit" required="false" max="10" />
<rule avp="Service-Information" required="false" max="1" />
<rule avp="Called-Station-Id" required="false" max="1" />
<rule avp="3GPP-Charging-Characteristics" required="false" max="1" />
<rule avp="3GPP-Charging-ID" required="false" max="1" />
<rule avp="3GPP-QoS-Negotiated-Profile" required="false" max="1" />
<rule avp="3GPP-IMSI" required="false" max="1" />
<rule avp="3GPP-NSAPI" required="false" max="1" />
<rule avp="3GPP-Selection-Mode" required="false" max="1" />
<rule avp="Rulebase-Id" required="false" max="1" />
<rule avp="Session-Start-Indicator" required="false" max="1" />
<rule avp="Framed-IP-Address" required="false" max="1" />
<rule avp="Framed-IPv6-Prefix" required="false" max="10" />
<rule avp="Framed-Interface-ID" required="false" max="1" />
<rule avp="3GPP-MS-TimeZone" required="false" max="1" />
<rule avp="3GPP-CG-Address" required="false" max="1" />
<rule avp="3GPP-SGSN-Address" required="false" max="1" />
<rule avp="3GPP-GGSN-Address" required="false" max="1" />
<rule avp="3GPP-IMSI-MCC-MNC" required="false" max="1" />
<rule avp="3GPP-SGSN-MCC-MNC" required="false" max="1" />
<rule avp="3GPP-GGSN-MCC-MNC" required="false" max="1" />
<rule avp="3GPP-CG-IPv6-Address" required="false" max="1" />
<rule avp="3GPP-GGSN-IPv6-Address" required="false" max="1" />
<rule avp="3GPP-PDP-Type" required="false" max="1" />
<rule avp="3GPP-Session-Stop-Indicator" required="false" max="1" />
<rule avp="3GPP-SGSN-IPv6-Address" required="false" max="1" />
<rule avp="Context-Type" required="false" max="1" />
<rule avp="User-Location-Information" required="false" max="1" />
<rule avp="Radio-Access-Technology" required="false" max="1" />
<rule avp="User-Name" required="false" max="1" />
<rule avp="CC-Sub-Session-Id" required="false" max="1" />
<rule avp="Acct-Multi-Session-Id" required="false" max="1" />
<rule avp="Origin-State-Id" required="false" max="1" />
<rule avp="Requested-Action" required="false" max="1" />
<rule avp="Multiple-Services-Indicator" required="false" max="1" />
<rule avp="Multiple-Services-Credit-Control" required="false" max="10" />
<rule avp="Service-Parameter-Info" required="false" max="1" />
<rule avp="CC-Correlation-Id" required="false" max="1" />
<rule avp="User-Equipment-Info" required="false" max="1" />
<rule avp="Proxy-Info" required="false" max="1" />
<rule avp="Route-Record" required="false" max="1" />
</request>
<answer>
<!-- http://tools.ietf.org/html/rfc4006#section-3.2 -->
<rule avp="Session-Id" required="true" max="1" />
<rule avp="Result-Code" required="true" max="1" />
<rule avp="Origin-Host" required="true" max="1" />
<rule avp="Origin-Realm" required="true" max="1" />
<rule avp="Auth-Application-Id" required="false" max="1" />
<rule avp="CC-Request-Type" required="true" max="1" />
<rule avp="CC-Request-Number" required="true" max="1" />
<rule avp="Event-Timestamp" required="false" max="1" />
<rule avp="Granted-Service-Unit" required="false" max="1" />
<rule avp="Final-Unit-Indication" required="false" max="1" />
<rule avp="Service-Information" required="false" max="1" />
<rule avp="User-Name" required="false" max="1" />
<rule avp="CC-Session-Failover" required="false" max="1" />
<rule avp="CC-Sub-Session-Id" required="false" max="1" />
<rule avp="Acct-Multi-Session-Id" required="false" max="1" />
<rule avp="Origin-State-Id" required="false" max="1" />
<rule avp="Multiple-Services-Credit-Control" required="false" max="10" />
<rule avp="Cost-Information" required="false" max="1" />
<rule avp="Check-Balance-Result" required="false" max="1" />
<rule avp="Credit-Control-Failure-Handling" required="false" max="1" />
<rule avp="Direct-Debiting-Failure-Handling" required="false" max="1" />
<rule avp="Validity-Time" required="false" max="1" />
<rule avp="Redirect-Host" required="false" max="1" />
<rule avp="Redirect-Host-Usage" required="false" max="1" />
<rule avp="Redirect-Max-Cache-Time" required="false" max="1" />
<rule avp="Proxy-Info" required="false" max="1" />
<rule avp="Route-Record" required="false" max="1" />
<rule avp="Failed-AVP" required="false" max="10" />
<rule avp="CC-Session-Failover" required="false" max="1" />
<rule avp="Subscription-Id" required="false" max="2" />
<rule avp="Rulebase-Id" required="false" max="1" />
</answer>
</command>
<avp name="CC-Correlation-Id" code="411" must="-" may="P,M" must-not="V" may-encrypt="Y">
<!-- http://tools.ietf.org/html/rfc4006#section-8.1 -->
<data type="OctetString" />

View File

@@ -244,7 +244,7 @@ func IfaceAsString(fld interface{}) (out string, err error) {
case int64:
return strconv.FormatInt(fld.(int64), 10), nil
case uint32:
return strconv.FormatUint(uint64(fld.(int32)), 10), nil
return strconv.FormatUint(uint64(fld.(uint32)), 10), nil
case uint64:
return strconv.FormatUint(fld.(uint64), 10), nil
case bool: