From 9bd7ef2c376ae02d765495d83eef5e106f327ed8 Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 31 Jul 2015 16:29:46 +0200 Subject: [PATCH] SM-FreeSWITCH: fix postpaid calls not being allowed out, Various local test fixes and preparations for release --- data/conf/samples/tutlocal/cgrates.json | 15 +- data/tariffplans/tutorial/Users.csv | 2 + .../cgrates/etc/cgrates/cgrates.json | 47 ++++- .../freeswitch/etc/freeswitch_conf.tar.gz | Bin 27494 -> 28272 bytes .../kamevapi/cgrates/etc/cgrates/cgrates.json | 183 ++---------------- .../etc/kamailio/kamailio-cgrates.cfg | 2 +- .../kamailio/etc/kamailio/kamailio.cfg | 11 +- .../cgrates/etc/cgrates/cgrates.json | 49 ++++- .../opensips/etc/opensips/opensips.cfg | 25 ++- engine/cdrs.go | 1 - engine/responder.go | 4 + engine/users.go | 2 +- general_tests/tutorial_fs_calls_test.go | 10 +- general_tests/tutorial_kam_calls_test.go | 2 - general_tests/tutorial_local_test.go | 30 +++ general_tests/tutorial_osips_calls_test.go | 6 +- sessionmanager/fssessionmanager.go | 16 +- 17 files changed, 183 insertions(+), 222 deletions(-) diff --git a/data/conf/samples/tutlocal/cgrates.json b/data/conf/samples/tutlocal/cgrates.json index d4567a697..a0d4d04ce 100644 --- a/data/conf/samples/tutlocal/cgrates.json +++ b/data/conf/samples/tutlocal/cgrates.json @@ -13,12 +13,12 @@ "rater": { "enabled": true, // enable Rater service: "cdrstats": "internal", // address where to reach the cdrstats service, empty to disable stats functionality<""|internal|x.y.z.y:1234> - "users": "internal", + "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> }, "scheduler": { "enabled": true, // start Scheduler service: - "save_interval": "5s", }, "cdrs": { @@ -31,8 +31,15 @@ "enabled": true, // starts the cdrstats service: }, -"users": { - "enabled": true, +"pubsubs": { + "enabled": true, // starts PubSub service: . }, + +"users": { + "enabled": true, // starts User service: . + "indexes": ["Uuid"], // user profile field indexes +}, + + } diff --git a/data/tariffplans/tutorial/Users.csv b/data/tariffplans/tutorial/Users.csv index 75936aa6a..1805a970f 100644 --- a/data/tariffplans/tutorial/Users.csv +++ b/data/tariffplans/tutorial/Users.csv @@ -2,5 +2,7 @@ cgrates.org,1001,SysUserName,danb cgrates.org,1001,SysPassword,hisPass321 cgrates.org,1001,Cli,+4986517174963 +cgrates.org,1001,Uuid,388539dfd4f5cefee8f488b78c6c244b9e19138e cgrates.org,1002,SysUserName,rif cgrates.org,1002,RifAttr,RifVal +cgrates.org,1002,Uuid,27f37edec0670fa34cf79076b80ef5021e39c5b5 diff --git a/data/tutorials/fs_evsock/cgrates/etc/cgrates/cgrates.json b/data/tutorials/fs_evsock/cgrates/etc/cgrates/cgrates.json index 45a900faf..8b3d98aa9 100644 --- a/data/tutorials/fs_evsock/cgrates/etc/cgrates/cgrates.json +++ b/data/tutorials/fs_evsock/cgrates/etc/cgrates/cgrates.json @@ -10,6 +10,9 @@ "rater": { "enabled": true, // enable Rater service: "cdrstats": "internal", // address where to reach the cdrstats service, empty to disable stats functionality<""|internal|x.y.z.y:1234> + "historys": "internal", // address where to reach the history service, empty to disable history 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> }, @@ -24,6 +27,7 @@ "cdrstats": "internal", // address where to reach the cdrstats service. Empty to disable stats gathering <""|internal|x.y.z.y:1234> }, + "cdrstats": { "enabled": true, // starts the cdrstats service: }, @@ -61,7 +65,34 @@ {"tag":"Cost", "cdr_field_id": "cost", "type": "cdrfield", "value": "cost"}, ], "trailer_fields": [], // template of the exported trailer fields - } + }, + "customer_tpl": { + "cdr_format": "csv", // exported CDRs format + "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": "/tmp/cgr_fsevsock/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":"AccId", "cdr_field_id": "accid", "type": "cdrfield", "value": "accid"}, + {"tag":"ReqType", "cdr_field_id": "reqtype", "type": "cdrfield", "value": "reqtype"}, + {"tag":"Tenant", "cdr_field_id": "tenant", "type": "cdrfield", "value": "tenant"}, + {"tag":"Category", "cdr_field_id": "category", "type": "cdrfield", "value": "category"}, + {"tag":"Subject", "cdr_field_id": "account", "type": "cdrfield", "value": "account"}, + {"tag":"Destination", "cdr_field_id": "destination", "type": "cdrfield", "value": "~destination:s/^1(\\d+)/+$1/:s/^\\+(\\d+)/00$1/"}, + {"tag":"AnswerTime", "cdr_field_id": "answer_time", "type": "cdrfield", "value": "answer_time", "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": [], + }, }, @@ -77,14 +108,20 @@ }, -"history_server": { - "enabled": true, // starts History service: . +"historys": { + "enabled": true, // starts History service: . "history_dir": "/tmp/cgr_fsevsock/cgrates/history", // location on disk where to store history files. }, -"history_agent": { - "enabled": true, // starts History as a client: . +"pubsubs": { + "enabled": true, // starts PubSub service: . +}, + + +"users": { + "enabled": true, // starts User service: . + "indexes": ["Uuid"], // user profile field indexes }, diff --git a/data/tutorials/fs_evsock/freeswitch/etc/freeswitch_conf.tar.gz b/data/tutorials/fs_evsock/freeswitch/etc/freeswitch_conf.tar.gz index 7349bae0c5dbb80410b6eb755b0cd6ab3500487c..f6212599410880d5e7d7a1a2f34eec63d79d5b6b 100644 GIT binary patch literal 28272 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�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 B5u^YB diff --git a/data/tutorials/kamevapi/cgrates/etc/cgrates/cgrates.json b/data/tutorials/kamevapi/cgrates/etc/cgrates/cgrates.json index dc7d624a9..770a0ea06 100644 --- a/data/tutorials/kamevapi/cgrates/etc/cgrates/cgrates.json +++ b/data/tutorials/kamevapi/cgrates/etc/cgrates/cgrates.json @@ -7,66 +7,12 @@ // This is what you get when you load CGRateS with an empty configuration file. -//"general": { -// "http_skip_tls_veify": false, // if enabled Http Client will accept any TLS certificate -// "rounding_decimals": 10, // system level precision for floats -// "dbdata_encoding": "msgpack", // encoding used to store object data in strings: -// "tpexport_dir": "/var/log/cgrates/tpe", // path towards export folder for offline Tariff Plans -// "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 -//}, - - -//"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 -//}, - - -//"rating_db": { -// "db_type": "redis", // rating subsystem database type: -// "db_host": "127.0.0.1", // rating subsystem database host address -// "db_port": 6379, // rating subsystem port to reach the database -// "db_name": "10", // rating subsystem database name to connect to -// "db_user": "", // rating subsystem username to use when connecting to database -// "db_passwd": "", // rating subsystem password to use when connecting to database -//}, - - -//"accounting_db": { -// "db_type": "redis", // accounting subsystem database: -// "db_host": "127.0.0.1", // accounting subsystem database host address -// "db_port": 6379, // accounting subsystem port to reach the database -// "db_name": "11", // accounting subsystem database name to connect to -// "db_user": "", // accounting subsystem username to use when connecting to database -// "db_passwd": "", // accounting subsystem password to use when connecting to database -//}, - - -//"stor_db": { -// "db_type": "mysql", // stor database type to use: -// "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": 0, // maximum database connections opened -// "max_idle_conns": -1, // maximum database connections idle -//}, - - -//"balancer": { -// "enabled": false, // start Balancer service: -//}, - - "rater": { "enabled": true, // enable Rater service: -// "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> + "cdrstats": "internal", // address where to reach the cdrstats service, empty to disable stats functionality<""|internal|x.y.z.y:1234> + "historys": "internal", // address where to reach the history service, empty to disable history 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> }, @@ -77,35 +23,13 @@ "cdrs": { "enabled": true, // start the CDR Server service: -// "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> - "cdrstats": "internal", // address where to reach the cdrstats service, empty to disable stats functionality<""|internal|x.y.z.y:1234> -// "reconnects": 5, // number of reconnect attempts to rater or cdrs -// "cdr_replication":[], // replicate the raw CDR to a number of servers + "rater": "internal", // address where to reach the Rater for cost calculation: <""|internal|x.y.z.y:1234> + "cdrstats": "internal", // address where to reach the cdrstats service. Empty to disable stats gathering <""|internal|x.y.z.y:1234> }, + "cdrstats": { "enabled": true, // starts the cdrstats service: -// "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 -// "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_interval": [], // filter on CDR Cost }, @@ -172,103 +96,28 @@ }, -//"cdrc": { -// "*default": { -// "enabled": false, // enable CDR client functionality -// "cdrs_address": "internal", // address where to reach CDR server. -// "cdr_format": "csv", // CDR file format -// "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 -// "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 -// "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 -// "content_fields":[ // import 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": "setup_time", "type": "cdrfield", "value": "11", "mandatory": true}, -// {"tag": "answer_time", "cdr_field_id": "answer_time", "type": "cdrfield", "value": "12", "mandatory": true}, -// {"tag": "usage", "cdr_field_id": "usage", "type": "cdrfield", "value": "13", "mandatory": true}, -// ], -// }, -//}, - - -//"sm_freeswitch": { -// "enabled": false, // starts SessionManager service: -// "rater": "internal", // address where to reach the Rater <""|internal|127.0.0.1:2013> -// "cdrs": "", // address where to reach CDR Server, empty to disable CDR capturing <""|internal|x.y.z.y:1234> -// "reconnects": 5, // number of reconnect attempts to rater or cdrs -// "cdr_extra_fields": [], // extra fields to store in CDRs in case of processing 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) -// "connections":[ // instantiate connections to multiple FreeSWITCH servers -// {"server": "127.0.0.1:8021", "password": "ClueCon", "reconnects": 5} -// ], -//}, - - "sm_kamailio": { "enabled": true, // starts SessionManager service: -// "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 reconnect attempts to rater or cdrs "create_cdr": true, // 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} // reconnects -1 to indefinitely connect -// ], }, -//"sm_opensips": { -// "enabled": false, // starts SessionManager service: -// "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": "", // address where to reach CDR Server, empty to disable CDR capturing <""|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 -// "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 -// "reconnects": -1, // reconnects -1 to indefinitely connect -//}, - - - -"history_server": { - "enabled": true, // starts History service: . +"historys": { + "enabled": true, // starts History service: . "history_dir": "/tmp/cgr_kamevapi/cgrates/history", // location on disk where to store history files. -// "save_interval": "1s", // interval to save changed cache into .git archive }, -"history_agent": { - "enabled": true, // starts History as a client: . -// "server": "internal", // address where to reach the master history server: +"pubsubs": { + "enabled": true, // starts PubSub service: . }, -//"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": true, // starts User service: . + "indexes": ["Uuid"], // user profile field indexes +}, + } diff --git a/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio-cgrates.cfg b/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio-cgrates.cfg index 954034144..9a837fd5e 100644 --- a/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio-cgrates.cfg +++ b/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio-cgrates.cfg @@ -103,7 +103,7 @@ route[CGR_LCR_REPLY] { route[CGR_SESSION_DISCONNECT] { json_get_field("$evapi(msg)", "HashEntry", "$var(HashEntry)"); json_get_field("$evapi(msg)", "HashId", "$var(HashId)"); - son_get_field("$evapi(msg)", "Reason", "$var(Reason)"); + json_get_field("$evapi(msg)", "Reason", "$var(Reason)"); jsonrpc_exec('{"jsonrpc":"2.0","id":1, "method":"dlg.end_dlg","params":[$(var(HashEntry){s.rm,"}),$(var(HashId){s.rm,"})]}'); #$jsonrpl($var(reply)); } diff --git a/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio.cfg b/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio.cfg index 6ae550efd..a61eea930 100644 --- a/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio.cfg +++ b/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio.cfg @@ -200,9 +200,14 @@ route[CGRATES_AUTH_REPLY] { sl_send_reply("503","CGR_ERROR"); exit; } - if $var(CgrMaxSessionTime) != -1 && !dlg_set_timeout("$var(CgrMaxSessionTime)") { - sl_send_reply("503","CGR_MAX_SESSION_TIME_ERROR"); - exit; + if $var(CgrMaxSessionTime) != -1 { + if $var(CgrMaxSessionTime) == 0 { // Not enough balance, do not allow the call to go through + sl_send_reply("403","Insufficient credit"); + exit; + } else if !dlg_set_timeout("$var(CgrMaxSessionTime)") { + sl_send_reply("503","CGR_MAX_SESSION_TIME_ERROR"); + exit; + } } if $var(CgrSuppliers) != "" { # Enforce the supplier variable to the first one received from CGRateS, more for testing purposes $dlg_var(cgrSupplier) = $(var(CgrSuppliers){s.select,0,,}); diff --git a/data/tutorials/osips_async/cgrates/etc/cgrates/cgrates.json b/data/tutorials/osips_async/cgrates/etc/cgrates/cgrates.json index 288c1ada6..fc500ef2b 100644 --- a/data/tutorials/osips_async/cgrates/etc/cgrates/cgrates.json +++ b/data/tutorials/osips_async/cgrates/etc/cgrates/cgrates.json @@ -10,6 +10,9 @@ "rater": { "enabled": true, // enable Rater service: "cdrstats": "internal", // address where to reach the cdrstats service, empty to disable stats functionality<""|internal|x.y.z.y:1234> + "historys": "internal", // address where to reach the history service, empty to disable history 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> }, @@ -42,7 +45,7 @@ "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": "/tmp/cgr_fsevsock/cgrates/cdre", // path where the exported CDRs will be placed + "export_dir": "/tmp/cgr_osipsasync/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"}, @@ -62,7 +65,34 @@ {"tag":"Cost", "cdr_field_id": "cost", "type": "cdrfield", "value": "cost"}, ], "trailer_fields": [], // template of the exported trailer fields - } + }, + "customer_tpl": { + "cdr_format": "csv", // exported CDRs format + "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": "/tmp/cgr_osipsasync/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":"AccId", "cdr_field_id": "accid", "type": "cdrfield", "value": "accid"}, + {"tag":"ReqType", "cdr_field_id": "reqtype", "type": "cdrfield", "value": "reqtype"}, + {"tag":"Tenant", "cdr_field_id": "tenant", "type": "cdrfield", "value": "tenant"}, + {"tag":"Category", "cdr_field_id": "category", "type": "cdrfield", "value": "category"}, + {"tag":"Subject", "cdr_field_id": "account", "type": "cdrfield", "value": "account"}, + {"tag":"Destination", "cdr_field_id": "destination", "type": "cdrfield", "value": "~destination:s/^1(\\d+)/+$1/:s/^\\+(\\d+)/00$1/"}, + {"tag":"AnswerTime", "cdr_field_id": "answer_time", "type": "cdrfield", "value": "answer_time", "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": [], + }, }, @@ -78,15 +108,20 @@ }, -"history_server": { - "enabled": true, // starts History service: . +"historys": { + "enabled": true, // starts History service: . "history_dir": "/tmp/cgr_osipsasync/cgrates/history", // location on disk where to store history files. }, -"history_agent": { - "enabled": true, // starts History as a client: . - "server": "internal", // address where to reach the master history server: +"pubsubs": { + "enabled": true, // starts PubSub service: . +}, + + +"users": { + "enabled": true, // starts User service: . + "indexes": ["Uuid"], // user profile field indexes }, diff --git a/data/tutorials/osips_async/opensips/etc/opensips/opensips.cfg b/data/tutorials/osips_async/opensips/etc/opensips/opensips.cfg index bb8b57d66..c3a8f2990 100644 --- a/data/tutorials/osips_async/opensips/etc/opensips/opensips.cfg +++ b/data/tutorials/osips_async/opensips/etc/opensips/opensips.cfg @@ -91,22 +91,22 @@ modparam("db_flatstore", "single_file", 1) loadmodule "acc.so" modparam("acc", "detect_direction", 1) #modparam("acc", "cdr_flag", "CDR") -#modparam("acc", "evi_flag", "CDR") -#modparam("acc", "evi_missed_flag", "CDR") +modparam("acc", "evi_flag", "CDR") +modparam("acc", "evi_missed_flag", "CDR") modparam("acc", "evi_extra", "cgr_reqtype=$avp(cgr_reqtype); cgr_account=$avp(cgr_account); cgr_destination=$avp(cgr_destination); cgr_supplier=$avp(cgr_supplier); dialog_id=$DLG_did") -modparam("acc", "db_url", "flatstore:/tmp") -modparam("acc", "db_flag", "CDR") -modparam("acc", "db_missed_flag", "CDR") -modparam("acc", "db_table_missed_calls", "cgr_missed") -modparam("acc", "db_extra", "cgr_reqtype=$avp(cgr_reqtype); - cgr_account=$avp(cgr_account); - cgr_destination=$avp(cgr_destination); - cgr_supplier=$avp(cgr_supplier); - dialog_id=$DLG_did") +#modparam("acc", "db_url", "flatstore:/tmp") +#modparam("acc", "db_flag", "CDR") +#modparam("acc", "db_missed_flag", "CDR") +#modparam("acc", "db_table_missed_calls", "cgr_missed") +#modparam("acc", "db_extra", "cgr_reqtype=$avp(cgr_reqtype); +# cgr_account=$avp(cgr_account); +# cgr_destination=$avp(cgr_destination); +# cgr_supplier=$avp(cgr_supplier); +# dialog_id=$DLG_did") #### CfgUtils module loadmodule "cfgutils.so" @@ -358,9 +358,6 @@ route[location] { t_reply("404", "Not Found"); exit; } - append_branch(); - append_branch(); - setflag(CDR); } failure_route[missed_call] { diff --git a/engine/cdrs.go b/engine/cdrs.go index 5a0ad538e..39f3b1c9c 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -324,7 +324,6 @@ func (self *CdrServer) rateCDR(storedCdr *StoredCdr) error { func (self *CdrServer) replicateCdr(cdr *StoredCdr) error { Logger.Debug(fmt.Sprintf("replicateCdr cdr: %+v, configuration: %+v", cdr, self.cgrCfg.CDRSCdrReplication)) for _, rplCfg := range self.cgrCfg.CDRSCdrReplication { - Logger.Debug(fmt.Sprintf("Replicating CDR with configuration: %+v", rplCfg)) passesFilters := true for _, cdfFltr := range rplCfg.CdrFilter { if fltrPass, _ := cdr.PassesFieldFilter(cdfFltr); !fltrPass { diff --git a/engine/responder.go b/engine/responder.go index 7d89c10ae..5e860cee6 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -45,21 +45,25 @@ type Responder struct { ExitChan chan bool CdrSrv *CdrServer Stats StatsInterface + cnt int64 } /* RPC method thet provides the external RPC interface for getting the rating information. */ func (rs *Responder) GetCost(arg *CallDescriptor, reply *CallCost) (err error) { + rs.cnt += 1 if arg.Subject == "" { arg.Subject = arg.Account } + Logger.Debug(fmt.Sprintf("CD before load user profile: %+v, count: %d", arg, rs.cnt)) if upData, err := LoadUserProfile(arg, "ExtraFields"); err != nil { return err } else { udRcv := upData.(*CallDescriptor) *arg = *udRcv } + Logger.Debug(fmt.Sprintf("CD after load user profile: %+v, count: %d", arg, rs.cnt)) if rs.Bal != nil { r, e := rs.getCallCost(arg, "Responder.GetCost") *reply, err = *r, e diff --git a/engine/users.go b/engine/users.go index 0cf821c74..ab334a9ad 100644 --- a/engine/users.go +++ b/engine/users.go @@ -186,7 +186,7 @@ func (um *UserMap) GetUsers(up UserProfile, results *UserProfiles) error { } } - var candidates UserProfiles + candidates := make(UserProfiles, 0) // It should not return nil in case of no users but [] for key, values := range table { ponder := 0 tableUP := &UserProfile{ diff --git a/general_tests/tutorial_fs_calls_test.go b/general_tests/tutorial_fs_calls_test.go index b55d1dbfe..fbdb3c590 100644 --- a/general_tests/tutorial_fs_calls_test.go +++ b/general_tests/tutorial_fs_calls_test.go @@ -326,7 +326,7 @@ func TestTutFsCallsCdrs(t *testing.T) { if reply[0].ReqType != utils.META_PREPAID { t.Errorf("Unexpected ReqType for CDR: %+v", reply[0]) } - if reply[0].Usage != "65" { // Usage as seconds + if reply[0].Usage != "65" && reply[0].Usage != "66" { // Usage as seconds t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) } if reply[0].Cost != 0 { // Cost was not calculated @@ -386,7 +386,7 @@ func TestTutFsCallsCdrs(t *testing.T) { if reply[0].Destination != "1001" { t.Errorf("Unexpected Destination for CDR: %+v", reply[0]) } - if reply[0].Usage != "63" { // Usage as seconds + if reply[0].Usage != "63" && reply[0].Usage != "64" { // Usage as seconds, sometimes takes a second longer to disconnect t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) } } @@ -405,7 +405,7 @@ func TestTutFsCallsCdrs(t *testing.T) { if reply[0].Destination != "1001" { t.Errorf("Unexpected Destination for CDR: %+v", reply[0]) } - if reply[0].Usage != "62" { // Usage as seconds + if reply[0].Usage != "62" && reply[0].Usage != "63" { // Usage as seconds t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) } } @@ -424,7 +424,7 @@ func TestTutFsCallsCdrs(t *testing.T) { if reply[0].Destination != "1002" { t.Errorf("Unexpected Destination for CDR: %+v", reply[0]) } - if reply[0].Usage != "64" { // Usage as seconds + if reply[0].Usage != "64" && reply[0].Usage != "65" { // Usage as seconds t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) } if reply[0].Cost == -1.0 { // Cost was not calculated @@ -446,7 +446,7 @@ func TestTutFsCallsCdrs(t *testing.T) { if reply[0].Destination != "1002" { t.Errorf("Unexpected Destination for CDR: %+v", reply[0]) } - if reply[0].Usage != "66" { // Usage as seconds + if reply[0].Usage != "66" && reply[0].Usage != "67" { // Usage as seconds t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) } if reply[0].Cost == -1.0 { // Cost was not calculated diff --git a/general_tests/tutorial_kam_calls_test.go b/general_tests/tutorial_kam_calls_test.go index 93d966410..a4dba99b3 100644 --- a/general_tests/tutorial_kam_calls_test.go +++ b/general_tests/tutorial_kam_calls_test.go @@ -595,7 +595,6 @@ func TestTutKamCallsStopPjsuaListener(t *testing.T) { time.Sleep(time.Duration(1) * time.Second) // Allow pjsua to finish it's tasks, eg un-REGISTER } -/* func TestTutKamCallsStopCgrEngine(t *testing.T) { if !*testCalls { return @@ -611,4 +610,3 @@ func TestTutKamCallsStopKam(t *testing.T) { } engine.KillProcName("kamailio", 1000) } -*/ diff --git a/general_tests/tutorial_local_test.go b/general_tests/tutorial_local_test.go index adebc3cf0..adc3c1cd8 100644 --- a/general_tests/tutorial_local_test.go +++ b/general_tests/tutorial_local_test.go @@ -182,6 +182,18 @@ func TestTutLocalGetCachedItemAge(t *testing.T) { */ } +func TestTutLocalGetUsers(t *testing.T) { + if !*testLocal { + return + } + var users engine.UserProfiles + if err := tutLocalRpc.Call("UsersV1.GetUsers", engine.UserProfile{}, &users); err != nil { + t.Error("Got error on UsersV1.GetUsers: ", err.Error()) + } else if len(users) != 2 { + t.Error("Calling UsersV1.GetUsers got users:", len(users)) + } +} + // Check call costs func TestTutLocalGetCosts(t *testing.T) { if !*testLocal { @@ -206,6 +218,24 @@ func TestTutLocalGetCosts(t *testing.T) { } else if cc.Cost != 0.6 { t.Errorf("Calling Responder.GetCost got callcost: %v", cc.Cost) } + // Make sure that the same cost is returned via users aliasing + cd = engine.CallDescriptor{ + Direction: "*out", + Category: "call", + Tenant: utils.USERS, + Subject: utils.USERS, + Account: utils.USERS, + Destination: "1002", + DurationIndex: 0, + TimeStart: tStart, + TimeEnd: tEnd, + ExtraFields: map[string]string{"Uuid": "388539dfd4f5cefee8f488b78c6c244b9e19138e"}, + } + if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil { + t.Error("Got error on Responder.GetCost: ", err.Error()) + } else if cc.Cost != 0.6 { + t.Errorf("Calling Responder.GetCost got callcost: %v", cc.Cost) + } tStart, _ = utils.ParseDate("2014-08-04T13:00:00Z") tEnd, _ = utils.ParseDate("2014-08-04T13:01:25Z") cd = engine.CallDescriptor{ diff --git a/general_tests/tutorial_osips_calls_test.go b/general_tests/tutorial_osips_calls_test.go index f3195522e..31b8b0b60 100644 --- a/general_tests/tutorial_osips_calls_test.go +++ b/general_tests/tutorial_osips_calls_test.go @@ -71,7 +71,6 @@ func TestTutOsipsCallsResetStorDb(t *testing.T) { } } -/* // start Kam server func TestTutOsipsCallsStartOsips(t *testing.T) { if !*testCalls { @@ -82,7 +81,6 @@ func TestTutOsipsCallsStartOsips(t *testing.T) { t.Fatal(err) } } -*/ // Start CGR Engine func TestTutOsipsCallsStartEngine(t *testing.T) { @@ -358,7 +356,7 @@ func TestTutOsipsCallsCdrs(t *testing.T) { if reply[0].Destination != "1001" { t.Errorf("Unexpected Destination for CDR: %+v", reply[0]) } - if reply[0].Usage != "62" { // Usage as seconds + if reply[0].Usage != "62" && reply[0].Usage != "63" { // Usage as seconds t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) } } @@ -448,11 +446,9 @@ func TestTutOsipsCallsStopCgrEngine(t *testing.T) { } } -/* func TestTutOsipsCallsStopOpensips(t *testing.T) { if !*testCalls { return } engine.KillProcName("opensips", 100) } -*/ diff --git a/sessionmanager/fssessionmanager.go b/sessionmanager/fssessionmanager.go index 2a082891a..913cc0ab7 100644 --- a/sessionmanager/fssessionmanager.go +++ b/sessionmanager/fssessionmanager.go @@ -220,13 +220,15 @@ func (sm *FSSessionManager) onChannelPark(ev engine.Event, connId string) { if err := sm.rater.GetDerivedMaxSessionTime(ev.AsStoredCdr(), &maxCallDuration); err != nil { engine.Logger.Err(fmt.Sprintf(" Could not get max session time for %s, error: %s", ev.GetUUID(), err.Error())) } - maxCallDur := time.Duration(maxCallDuration) - if maxCallDur <= sm.cfg.MinCallDuration { - //engine.Logger.Info(fmt.Sprintf("Not enough credit for trasferring the call %s for %s.", ev.GetUUID(), cd.GetKey(cd.Subject))) - sm.unparkCall(ev.GetUUID(), connId, ev.GetCallDestNr(utils.META_DEFAULT), INSUFFICIENT_FUNDS) - return + if maxCallDuration != -1 { // For calls different than unlimited, set limits + maxCallDur := time.Duration(maxCallDuration) + if maxCallDur <= sm.cfg.MinCallDuration { + //engine.Logger.Info(fmt.Sprintf("Not enough credit for trasferring the call %s for %s.", ev.GetUUID(), cd.GetKey(cd.Subject))) + sm.unparkCall(ev.GetUUID(), connId, ev.GetCallDestNr(utils.META_DEFAULT), INSUFFICIENT_FUNDS) + return + } + sm.setMaxCallDuration(ev.GetUUID(), connId, maxCallDur) } - sm.setMaxCallDuration(ev.GetUUID(), connId, maxCallDur) // ComputeLcr if ev.ComputeLcr() { cd, err := fsev.AsCallDescriptor() @@ -341,7 +343,7 @@ func (sm *FSSessionManager) Shutdown() (err error) { continue } engine.Logger.Info(fmt.Sprintf(" Shutting down all sessions on connection id: %s", connId)) - if _, err = fSock.SendApiCmd("hupall MANAGER_REQUEST cgr_reqtype prepaid"); err != nil { + if _, err = fSock.SendApiCmd("hupall MANAGER_REQUEST cgr_reqtype *prepaid"); err != nil { engine.Logger.Err(fmt.Sprintf(" Error on calls shutdown: %s, connection id: %s", err.Error(), connId)) } }