2582 Commits

Author SHA1 Message Date
DanB
cc3bebd90c Correcting tutorial for opensips installation 2015-09-22 12:03:48 +02:00
DanB
2ce2cbbb27 Tutorial documentation updates, simplified install for Kamailio and FreeSWITCH 2015-09-22 11:42:00 +02:00
DanB
0df9f63409 Tutorial TP - Renamed *Tag into *Id so we do not create confusion with using both naming 2015-09-22 11:33:43 +02:00
Radu Ioan Fericean
389b6cac21 hide masked user
fixes #185, fixes #183
2015-09-22 11:23:38 +03:00
Radu Ioan Fericean
7b53d35a7a added alownegative,disabled to accountactions
also started maked for users
2015-09-21 16:29:58 +03:00
Radu Ioan Fericean
ec5edea780 fixed action trigger related local tests 2015-09-21 14:04:50 +03:00
DanB
6ec74c44ba Fix package building using glide, removing glide_enable/disable scripts 2015-09-21 09:43:07 +02:00
Radu Ioan Fericean
f886c79bab added console test in test.sh 2015-09-21 09:54:13 +03:00
DanB
023c6406cc Make sure glide path is dynamic so we can use it in debian/rules, fixes #182 2015-09-20 16:54:13 +02:00
DanB
e464bd1f67 Add SetAccount with ActionTriggersId and Disable/AllowNegative tests 2015-09-20 16:15:42 +02:00
DanB
e3de27ec06 Fix build, rename ActionTriggerPriotityList to ActionTriggers 2015-09-20 13:56:39 +02:00
Radu Ioan Fericean
2a99f63827 add action triggers id to set account api 2015-09-20 10:54:10 +03:00
Radu Ioan Fericean
991fc09884 save action triggers to ratingdb 2015-09-20 10:42:53 +03:00
Radu Ioan Fericean
ac7eb6fbaf trim spaces for balance dest ids 2015-09-20 10:11:37 +03:00
DanB
d43cf8c927 Small Fix call tests 2015-09-19 21:54:56 +02:00
DanB
2a51356e5b Fix tests for aliased CDRs in kamailio tutorial 2015-09-19 21:23:00 +02:00
DanB
50197116df Moved aliasing for CDRs into CdrServer to have it centralized for other methods pushing CDRs 2015-09-19 21:21:22 +02:00
DanB
5fcc2c9d9c Revert SM connections to single instead of pool 2015-09-19 20:47:45 +02:00
DanB
9daa3ae4e0 Fix rater and cdrs connection in pool for session managers 2015-09-19 19:58:53 +02:00
DanB
99076bb7e4 Old aliases compatibility added back in apier, fix create_tariffplan_tables for both mysql and postgres, fix local_tests 2015-09-19 18:54:07 +02:00
DanB
6aedf2b507 Revert SM configuration for non ha rater and cdrs connections 2015-09-19 16:04:49 +02:00
DanB
3e4e6ab0c9 Fix debian package build, rename glide_up.sh into glide_enable.sh and adding glide_disable.sh 2015-09-19 14:21:11 +02:00
DanB
64c4386d4a glide_up.sh 2015-09-19 13:41:15 +02:00
Radu Ioan Fericean
681d282662 updated sql files for balance disabled field 2015-09-18 14:48:45 +03:00
Radu Ioan Fericean
33bb801340 fixed mandatory data for settplcrrule api 2015-09-18 14:45:29 +03:00
Radu Ioan Fericean
4c7e6b7b77 test for identical uuid in different account 2015-09-18 14:28:30 +03:00
Radu Ioan Fericean
116539c3e5 docker using glide 2015-09-18 14:03:07 +03:00
Radu Ioan Fericean
631ca167a6 added LoadAlias method and used it in responder
fixes #177
2015-09-18 13:32:19 +03:00
Radu Ioan Fericean
7a93142d45 use glide up instead of update 2015-09-18 13:07:27 +03:00
Radu Ioan Fericean
5229e616f8 splited glide yaml files 2015-09-18 11:14:40 +03:00
Radu Ioan Fericean
a563572a78 fixed doc links 2015-09-17 22:14:30 +03:00
Radu Ioan Fericean
2e30387da2 added documentation for building using glide
fixes #179
2015-09-17 22:10:38 +03:00
Radu Ioan Fericean
03b31047f3 aliases interface loading and tests 2015-09-17 19:26:05 +03:00
Radu Ioan Fericean
10fed0f108 package name 2015-09-17 14:03:25 +03:00
Radu Ioan Fericean
95a091f936 started using glide for vendor packages 2015-09-17 13:25:30 +03:00
Radu Ioan Fericean
8de86b3e43 better aliases loader 2015-09-17 12:34:51 +03:00
Radu Ioan Fericean
6004432c3e fixed mock connector tests 2015-09-16 21:43:49 +03:00
Radu Ioan Fericean
2709042357 used new aliases for rating profile finding
before mass reflection replacement
2015-09-16 21:30:44 +03:00
Radu Ioan Fericean
55aa957691 fix duplicate balance uuid issue 2015-09-16 18:18:12 +03:00
Radu Ioan Fericean
3b9ff9ea7e commented out failing apis 2015-09-16 14:51:40 +03:00
Radu Ioan Fericean
54b42958aa added target and original value for aliases 2015-09-16 14:00:32 +03:00
Radu Ioan Fericean
af834baaa4 fixed tests 2015-09-15 14:51:45 +03:00
Radu Ioan Fericean
351a029fde Merge branch 'master' into hapool 2015-09-15 14:26:02 +03:00
Radu Ioan Fericean
1154c25f81 added enabledisablebalance action
fixes #174
2015-09-15 14:24:49 +03:00
Radu Ioan Fericean
4aae17773d use destination ids in cdr stats
fixes #173
2015-09-14 22:28:30 +03:00
Radu Ioan Fericean
46176699e2 fix for scheduler multiple execution
fixes #175
2015-09-14 21:22:12 +03:00
Radu Ioan Fericean
fd63470916 added timer for guardian lock
fixes #172
2015-09-14 21:22:12 +03:00
DanB
e4f2441e0e Fixups aliases old implementation emulation 2015-09-14 15:48:04 +02:00
DanB
89dc80e95f Merge branch 'master' of https://github.com/cgrates/cgrates 2015-09-14 13:23:28 +02:00
Radu Ioan Fericean
bf1658748c return slice of reverse aliases 2015-09-14 14:23:12 +03:00
DanB
a86fcad2ff Aliases old implementation compatibility, local tests modified 2015-09-14 13:22:50 +02:00
DanB
3bd40c6dbe Console cdre_config_reload command 2015-09-12 20:50:34 +02:00
DanB
b7f839de31 CDRE config reload, fixes #110, improved handling of the CDRC reloads without sync 2015-09-12 20:49:53 +02:00
DanB
e5f0380af6 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-09-11 19:11:37 +02:00
DanB
e8aea9f0d5 ApierV1.ReloadCdrcConfig command as well as console cdrc_config_reload 2015-09-11 19:11:25 +02:00
DanB
2047d881da CDRC runtime config reloads implementation 2015-09-11 19:10:42 +02:00
Radu Ioan Fericean
4d2112c409 fixed apis and tp structures 2015-09-10 18:30:37 +03:00
Radu Ioan Fericean
ffb86e703e fixes after merge 2015-09-08 18:03:42 +03:00
Radu Ioan Fericean
e50a0f8753 Merge branch 'master' into hapool 2015-09-08 16:37:32 +03:00
Radu Ioan Fericean
8a74a0a622 fixes for matching alias 2015-09-08 14:09:24 +03:00
Radu Ioan Fericean
ec040b4ae6 added reverse alias APIs and console commands
fixes #170, fixes #168
2015-09-07 17:22:54 +03:00
DanB
93a3f89428 Internal connection timeouts within rater start 2015-09-05 14:27:00 +02:00
DanB
0edbf9bde2 Small test fix to cope with go1.5 2015-09-05 12:25:46 +02:00
DanB
fd89caab02 cmd/cgr-engine - put back internal connections used by CDRS 2015-09-05 11:38:57 +02:00
Radu Ioan Fericean
a3a7b75fb0 verified new tps data matching tests,fixes #167 2015-09-04 17:50:41 +03:00
Radu Ioan Fericean
eb182825ed using destination id in revers aliases 2015-09-04 17:34:51 +03:00
Radu Ioan Fericean
0a1fc7d96d added new connections to cdrs service 2015-09-04 16:59:36 +03:00
Radu Ioan Fericean
964ced8432 tested reverse alias 2015-09-04 15:18:35 +03:00
Radu Ioan Fericean
c2cb492cbe started liases reverse mapping 2015-09-04 13:50:05 +03:00
DanB
2dbf8689b9 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-09-04 12:08:33 +02:00
DanB
84313bc87d cmd/cgr-engine using chans instead of sync.Group to synchronize dependencies 2015-09-04 12:07:45 +02:00
Radu Ioan Fericean
1c94fca45c test for TP aliases and users 2015-09-03 21:34:25 +03:00
DanB
7f0925c4e0 Use UTC everywhere we build cgrid out of setuptime 2015-09-03 12:30:07 +02:00
DanB
a1b4c49ed6 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-09-01 17:11:14 +02:00
DanB
2eade17397 Fix debian package build using build.sh 2015-09-01 17:11:08 +02:00
Radu Ioan Fericean
ee6055a743 updated installation command 2015-09-01 18:05:33 +03:00
DanB
9d300d1da3 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-09-01 16:24:22 +02:00
DanB
01f93562cb Fix error handling in SM remote connection, thanks @covalschi 2015-09-01 16:23:49 +02:00
Radu Ioan Fericean
18b41aabe4 importer no longer work in go 1.5 2015-08-28 17:27:49 +03:00
Radu Ioan Fericean
ffc30e43ed added TP apis for aliases, users and lcr rules
fixes #154, fixes #155, fixes #156
2015-08-28 17:18:52 +03:00
Radu Ioan Fericean
a8829de649 added placeholder back 2015-08-28 17:11:35 +03:00
Radu Ioan Fericean
a6b1481ca9 added extrafields name, fixes #165 2015-08-27 19:47:53 +03:00
Radu Ioan Fericean
189c284355 added go 1.5 to travis 2015-08-27 15:35:30 +03:00
DanB
b53922bfa1 Postpaid calls being checked by GetDerivedMaxSessionTime, should exist and have cost for the authorized call, fixes #140, fixes #138 2015-08-27 14:01:24 +02:00
DanB
8c8543ef3d Merge branch 'master' of https://github.com/cgrates/cgrates 2015-08-27 09:16:33 +02:00
DanB
abbbbf953d Unify reconnects configuration, fixes #153 2015-08-27 09:16:15 +02:00
Radu Ioan Fericean
d00c400276 use usage record pointer, fixes #164 2015-08-26 18:18:10 +03:00
DanB
22018d21ce Remove .Debug from CDRs 2015-08-26 15:45:21 +02:00
DanB
263311cab1 DefaultTimezone configuration fix for cdrs_primary table 2015-08-26 14:52:09 +02:00
DanB
a1911f293a Remove config test for history 2015-08-26 11:40:51 +02:00
DanB
eb9a6c132d Cache data only in components where is needed, finished redesign of the cgr-engine startup, fixes #160, fixes #136 2015-08-26 11:07:51 +02:00
DanB
d8781f7c32 Adding cmd/cgr-engine/rater.go 2015-08-25 20:46:03 +02:00
DanB
599a0c214e Redesign of cmd/cgr-engine using channels for intercommunication between services 2015-08-25 20:45:31 +02:00
DanB
5dd828810b Remove debug log from replicateCdr 2015-08-24 11:15:15 +02:00
DanB
cd2befb083 ApierV1.GetLoadHistory implementation, console load_history command 2015-08-24 11:09:44 +02:00
DanB
f2386c88f3 ApierV2.LoadTariffPlanFromFolder with support for returning loadInstance data, various localtest cache tests fixes 2015-08-23 15:48:14 +02:00
DanB
9d88fdc792 LoadHistory implementation, added LastLoadId and LastLoadTime to CacheStats 2015-08-23 14:21:44 +02:00
DanB
10d80eaaeb Enabling aliases service in tutorial configurations 2015-08-22 22:34:15 +02:00
DanB
2b3e5006c4 Old aliases in tutorial files 2015-08-22 22:23:45 +02:00
DanB
59fb172502 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-08-22 18:37:58 +02:00
DanB
fd1db53a97 utils.OUT for default direction 2015-08-22 18:37:28 +02:00
DanB
cf3cdc8530 Default fields fix for cdrs.processCdr 2015-08-22 18:31:34 +02:00
DanB
29a5b1e927 Usage and Pdd fields should not be rounded to .0 decimals 2015-08-22 11:28:22 +02:00
Radu Ioan Fericean
206a1dd960 started implementation of tpaliases and tpusers 2015-08-21 22:43:50 +03:00
Radu Ioan Fericean
bef775009a fixed build 2015-08-21 16:17:51 +03:00
Radu Ioan Fericean
7b03c0675a aliases in cgr-engine 2015-08-21 15:29:01 +03:00
Radu Ioan Fericean
818b45ae19 aliases config 2015-08-21 15:29:01 +03:00
DanB
36d8bac0b6 Adding LogCallCost method to CdrsV1 RPC 2015-08-20 15:20:10 +02:00
DanB
6434116c57 Fix remote method in CDRS, influenced double charging for prepaid calls 2015-08-20 14:41:47 +02:00
DanB
e8fb0e8395 Make sure empty subject will use account data in CDRS requests 2015-08-20 13:26:02 +02:00
DanB
ea8cef96f5 SM-FreeSWITCH - parse variable_* in extra fields 2015-08-20 12:14:40 +02:00
DanB
da8bf88aeb Merge branch 'master' of https://github.com/cgrates/cgrates 2015-08-20 11:51:44 +02:00
DanB
a4c59e2fea Remove old AccountAliases and RatingAliases from cache_item_age 2015-08-20 11:51:13 +02:00
DanB
57cf05ff46 cgr-loader parameter change, tariffplandb_* -> tpdb_* for usability purposes 2015-08-20 11:39:59 +02:00
Radu Ioan Fericean
ddf0f0291e use latest golang version in devel docker 2015-08-20 12:38:39 +03:00
DanB
efce8fa45d Renaming cgr-loader options, accountingdb->datadb, ratingdb->tariffplandb 2015-08-20 10:22:35 +02:00
DanB
7668631ad3 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-08-20 09:13:18 +02:00
DanB
8ddf45f64b SM-FreeSWITCH config change, cdr_extra_fields -> extra_fields, using them now also in auth and lcr requests, fixes #150 2015-08-20 09:13:10 +02:00
Radu Ioan Fericean
3d8d979540 adde GetMatchingAlias to alias API 2015-08-19 19:23:39 +03:00
Radu Ioan Fericean
c67bba595d fix local test 2015-08-19 15:53:32 +03:00
Radu Ioan Fericean
c025008e7f better ohmyzsh install 2015-08-19 14:54:23 +03:00
DanB
d782bb9bdb Postgresql table creation fix for aliases, RemRatingProfile fix on Tenant name 2015-08-19 13:52:36 +02:00
Radu Ioan Fericean
a009ede334 Merge branch 'master' into hapool 2015-08-19 13:59:38 +03:00
Radu Ioan Fericean
68f6b10be9 Merge branch 'master' into aliases 2015-08-19 13:56:36 +03:00
DanB
6b218912f8 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-08-19 12:15:49 +02:00
DanB
1efd09a655 Configurable default_timezone for timestamps which are missing it, fixes #149, fixes #108 2015-08-19 12:15:28 +02:00
Radu Ioan Fericean
59b73621d5 guard on remove rating profile api 2015-08-19 12:25:51 +03:00
Radu Ioan Fericean
7d20097754 no default fallback, fixes #146 2015-08-18 23:05:05 +03:00
Radu Ioan Fericean
7498edaaa1 Merge branch 'master' into hapool 2015-08-18 20:18:01 +03:00
Radu Ioan Fericean
287767d8fa Merge branch 'master' into aliases 2015-08-18 20:13:14 +03:00
Radu Ioan Fericean
522c296286 rem rating profile attribute check 2015-08-18 20:03:35 +03:00
Radu Ioan Fericean
891f407b54 added remove rating profile api + console 2015-08-18 19:19:56 +03:00
DanB
9db4948f3b Small fix tutorial fs 2015-08-18 13:38:04 +02:00
DanB
18c3161b8d Kamailio tutorial doc files 2015-08-18 13:06:05 +02:00
DanB
5149b080c0 New kamailio tutorial in docs, various doc improvements, fixes in local calls 2015-08-18 13:05:34 +02:00
DanB
7d8c9da2af ApierV1.SetAccount - add Disabled option, proper AllowNegative implementation 2015-08-17 13:06:52 +02:00
DanB
2aebd5e080 Improved tutorial calls tests 2015-08-17 12:50:33 +02:00
DanB
ad682a5aed LcrReq parameter change StartTime->SetupTime for uniformity with MaxUsageReq 2015-08-13 12:16:30 +02:00
Radu Ioan Fericean
3d4626de5a fix build for the demo 2015-08-13 12:18:01 +03:00
Radu Ioan Fericean
98a172ebbf apier load fixes 2015-08-12 15:41:14 +03:00
Radu Ioan Fericean
345b9cd22a Merge branch 'master' into aliases
Conflicts:
	general_tests/tutorial_local_test.go
2015-08-11 19:05:08 +03:00
Radu Ioan Fericean
548d01bff1 more tests fixes 2015-08-11 18:45:19 +03:00
Radu Ioan Fericean
9f2a36b0e5 compilation fixes 2015-08-11 16:44:53 +03:00
Radu Ioan Fericean
93c85c48be added update alias method 2015-08-10 19:27:37 +03:00
Radu Ioan Fericean
3d09334fff local test passes 2015-08-10 18:56:45 +03:00
Radu Ioan Fericean
241306a9ae moved aliases to cache 2015-08-10 18:02:53 +03:00
DanB
95694b688e LcrRequest.IgnoreErrors flag implementation, fixes #142 2015-08-10 15:47:38 +02:00
Radu Ioan Fericean
bfe866d57c passing engine tests 2015-08-10 16:07:43 +03:00
DanB
9b8afb675f Fix uninitialized ExtraFields in exporter 2015-08-06 16:56:53 +02:00
Radu Ioan Fericean
5b337db6ea Merge branch 'master' into aliases 2015-08-06 17:23:41 +03:00
Radu Ioan Fericean
767fd1fa84 work in progress, compilable version 2015-08-06 17:23:12 +03:00
Radu Ioan Fericean
7dec80663b fix for console pointer Paginator 2015-08-05 17:12:05 +03:00
DanB
5e3163f5c2 Do not provide Cost in *load_distribution strategy 2015-08-05 13:50:22 +02:00
DanB
04fa0d7e08 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-08-05 13:16:12 +02:00
DanB
313aab0952 Adding *load_distribution as strategy exemplified 2015-08-05 13:16:09 +02:00
Radu Ioan Fericean
6b5f15abcc unified lcr supplier format 2015-08-05 14:05:11 +03:00
Radu Ioan Fericean
1319531fb6 Merge pull request #141 from foehn/master
ActionsTriggers: Metrics in call_url* functions
2015-08-05 12:59:11 +03:00
Eloy Coto
37454f8736 ActionsTriggers: StatsQueueTriggered with public Metrics to get the info
in call_url and call_url_async
2015-08-05 10:17:52 +01:00
DanB
c638683756 Paginator for GetLcr requests 2015-08-04 21:38:05 +02:00
Radu Ioan Fericean
a17e8445cb better getlcr paginator 2015-08-04 19:39:38 +03:00
Radu Ioan Fericean
c80a6d7857 added paginator for GetLcr 2015-08-04 19:29:17 +03:00
Radu Ioan Fericean
41c76cc85f removed split on days 2015-08-04 19:15:32 +03:00
Radu Ioan Fericean
555b723a93 started aliases structures 2015-08-04 19:14:58 +03:00
DanB
a95a82bac8 Doc updates 2015-08-04 00:52:32 +02:00
DanB
e44dede8f9 Doc updates 2015-08-04 00:37:57 +02:00
DanB
30b72380b0 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-08-03 21:20:48 +02:00
DanB
30857b1fa8 Version changed to rc7 2015-08-03 21:20:40 +02:00
Radu Ioan Fericean
4ec469906d added Timeout field back 2015-08-03 21:06:37 +03:00
Radu Ioan Fericean
1bcc48aba8 Merge branch 'master' into hapool 2015-08-03 21:05:39 +03:00
Radu Ioan Fericean
f8d72138f3 improvement on timing contains date method 2015-08-03 20:57:26 +03:00
Radu Ioan Fericean
e3f4b21dac make sure the account is not cached in calldesc 2015-08-03 13:47:57 +03:00
Radu Ioan Fericean
872891d6d9 created method for account reset 2015-08-03 13:29:47 +03:00
Radu Ioan Fericean
5e20dadfba make sure account is not cached 2015-08-03 13:23:05 +03:00
Radu Ioan Fericean
39a51dc826 better rateinterval sorting 2015-08-03 11:50:56 +03:00
DanB
679b89d49b Adding Account and User attributes to tutorial/Users.csv, removing .Debug through code 2015-07-31 18:14:08 +02:00
Radu Ioan Fericean
8e997f0024 more on apier users load 2015-07-31 18:26:13 +03:00
Radu Ioan Fericean
064bbfc92f apier load tp from folder to load users 2015-07-31 18:07:08 +03:00
Radu Ioan Fericean
7b307dfc6d fixed user load hang 2015-07-31 17:53:29 +03:00
DanB
bac610094d Merge branch 'master' of https://github.com/cgrates/cgrates 2015-07-31 16:30:07 +02:00
DanB
9bd7ef2c37 SM-FreeSWITCH: fix postpaid calls not being allowed out, Various local test fixes and preparations for release 2015-07-31 16:29:46 +02:00
Radu Ioan Fericean
10b92844eb renamed share to ratio 2015-07-31 17:24:09 +03:00
Radu Ioan Fericean
de22799b5d renamed ponder to share 2015-07-31 17:04:15 +03:00
Radu Ioan Fericean
1fdd3ef654 flush users on reload 2015-07-31 16:54:59 +03:00
Radu Ioan Fericean
a09e24d5da fix for users csv load 2015-07-31 16:44:25 +03:00
Radu Ioan Fericean
aa3730d0cf moved users in accounting db 2015-07-31 15:51:42 +03:00
Radu Ioan Fericean
ef4d090d20 Merge branch 'master' into hapool 2015-07-31 14:30:48 +03:00
Radu Ioan Fericean
d4721b1753 added some load distribution lcr documentation 2015-07-31 14:16:32 +03:00
Radu Ioan Fericean
bc4fd873c5 load lcr missing ponders 2015-07-31 13:59:45 +03:00
Radu Ioan Fericean
0146267dc9 Merge branch 'master' into hapool 2015-07-31 12:49:25 +03:00
Radu Ioan Fericean
bc82a4165a one more test 2015-07-31 12:02:12 +03:00
Radu Ioan Fericean
6110c321b5 fixes and more tests for lcr 2015-07-31 11:56:26 +03:00
Radu Ioan Fericean
d4a440d0ec first working load distribution lcr 2015-07-31 11:28:08 +03:00
DanB
9176f56589 Properly export ExtraFields in UsageRequest and ExternalCdrs 2015-07-31 09:28:27 +02:00
DanB
d33ccd154b Merge branch 'master' of https://github.com/cgrates/cgrates 2015-07-31 09:16:08 +02:00
DanB
ab2b05dbde Adding CDRs posted information to CDRC, fixes #132 2015-07-31 09:12:25 +02:00
Radu Ioan Fericean
92218a62fd load distribution lcr (work in progress) 2015-07-30 23:12:15 +03:00
Radu Ioan Fericean
36f57b0e93 sync for user service 2015-07-30 18:40:23 +03:00
Radu Ioan Fericean
8a19b2736c fix form missing account alias key 2015-07-30 18:03:35 +03:00
DanB
ee62da445f <CDRC> CDR filters for fwv, fixes #135 2015-07-30 16:46:37 +02:00
DanB
44780f1fe3 Disable alias in prepaid1cent 2015-07-30 14:46:36 +02:00
DanB
186238a4fd Fix json configuration to parse rater.users, pubsubs, historys, add CdrStats and Users to CacheStats counters, fix #130, fix #131 2015-07-30 13:56:34 +02:00
DanB
a158793845 UserS methods in both CallDescriptor and StoredCdr, fixes #129 2015-07-29 17:11:40 +02:00
DanB
879b8faca2 Setting the documentation version 2015-07-29 13:21:06 +02:00
DanB
0261252b69 Add LoadUserProfile in the CDRs core 2015-07-29 12:32:17 +02:00
DanB
dbc6160e8a Only load user profile if at least one field requesting it 2015-07-29 12:25:48 +02:00
DanB
57ef37edca ExtraFields in CallDescriptor 2015-07-29 12:07:46 +02:00
Radu Ioan Fericean
0241f5a14b Merge branch 'master' into hapool 2015-07-29 12:57:35 +03:00
Radu Ioan Fericean
8086103036 fixed utils tests 2015-07-29 12:57:22 +03:00
Radu Ioan Fericean
f0fe0d9ef4 set extra fields from user profile too 2015-07-29 12:57:22 +03:00
Dan Christian Bogos
13d77363f5 Merge pull request #126 from foehn/master
Fix account command in the docs
2015-07-29 11:19:10 +03:00
DanB
4d7c49dba3 CDRC - Exclude normal .csv out of partial records checks, thanks @tomaszdomin-arevocom 2015-07-29 09:02:41 +02:00
DanB
2236be7939 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-07-28 18:55:21 +02:00
DanB
43c2575a04 Initial working CDRC .fwv implementation 2015-07-28 18:49:41 +02:00
DanB
3fdf084902 Defaults improved for StorDb max connections 2015-07-28 18:08:22 +02:00
Radu Ioan Fericean
4cb2ec80fe fix upper limit in GetAccounts 2015-07-28 15:35:57 +03:00
Radu Ioan Fericean
03f43dee37 more GetValue for balances 2015-07-28 14:21:41 +03:00
Radu Ioan Fericean
23593d585c better balance modyfy event 2015-07-28 14:17:34 +03:00
Radu Ioan Fericean
b06755245e fixes for local tests 2015-07-27 23:00:02 +03:00
Radu Ioan Fericean
20ed3ea34f simplified pubsub event 2015-07-27 22:13:40 +03:00
DanB
4a61f02b80 Adding conf samples for fwv tests 2015-07-27 20:52:31 +02:00
DanB
f839f1c9e8 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-07-27 20:49:00 +02:00
DanB
a0821c3134 CDRC - Offset handling and initial header, trailer and content processing for .fwv 2015-07-27 20:48:51 +02:00
Radu Ioan Fericean
4203a49ac8 better tests for max session time 2015-07-27 21:16:39 +03:00
Radu Ioan Fericean
f3257b5dba aliases tests 2015-07-27 20:50:14 +03:00
Radu Ioan Fericean
520f82c27a more aliases fixes 2015-07-27 19:04:33 +03:00
Radu Ioan Fericean
78c358e45b fixess for alias removal 2015-07-27 18:19:05 +03:00
Eloy Coto
88679ef858 Fix account command 2015-07-27 15:52:32 +01:00
Radu Ioan Fericean
ea4a6e6f6a ceche for not found err 2015-07-27 17:05:14 +03:00
Radu Ioan Fericean
ba7d40d749 fix wrong merge 2015-07-27 16:35:05 +03:00
Radu Ioan Fericean
28f01db6c9 Merge branch 'master' into hapool 2015-07-27 16:31:55 +03:00
Radu Ioan Fericean
b8e43f14f2 optimized alliases removal 2015-07-27 16:23:30 +03:00
Radu Ioan Fericean
b1b8c111e2 optimized add alias apis 2015-07-27 16:23:30 +03:00
DanB
b121b3e607 Adding missing files for travis build 2015-07-27 13:42:05 +02:00
DanB
6d1076870f CDRC config changes: renaming cdr_fields into content_fields, adding new header_fields and trailer_fields options 2015-07-27 13:09:54 +02:00
DanB
67ec46ccd9 Adding usage conversion back in auth.go 2015-07-26 21:11:22 +02:00
DanB
f1e0e71798 call_url should not double encode request 2015-07-26 20:25:18 +02:00
DanB
0afd47655a Merge branch 'master' of https://github.com/cgrates/cgrates 2015-07-26 18:56:21 +02:00
Radu Ioan Fericean
fd4d9b5ff8 removed logs 2015-07-26 19:45:01 +03:00
Radu Ioan Fericean
8c3e36c199 fix for 0 max session time 2015-07-26 19:34:17 +03:00
DanB
6e47749d89 CDRC refactoring to support plugable file processors 2015-07-26 18:33:59 +02:00
Radu Ioan Fericean
faa179f9cf show account too 2015-07-26 19:10:00 +03:00
Radu Ioan Fericean
b9b8876b37 more logging 2015-07-26 19:06:34 +03:00
Radu Ioan Fericean
3e5b5b9208 addded max session time logging and tests 2015-07-26 18:51:53 +03:00
Radu Ioan Fericean
4f11a40a57 moved response cache ttl in general config 2015-07-24 17:57:46 +03:00
DanB
df92a1453d Revert "Modifying CDRC file processing to accept sources other than .csv"
This reverts commit 51cb6c388f.
2015-07-24 15:06:19 +02:00
DanB
3b2668b22c Revert "Removing CDR replication debug statements"
This reverts commit d74784206b.
2015-07-24 15:05:29 +02:00
DanB
51cb6c388f Modifying CDRC file processing to accept sources other than .csv 2015-07-23 21:04:20 +02:00
Radu Ioan Fericean
c02ee71899 added HaPool cfg options 2015-07-23 16:51:04 +03:00
DanB
d938b08549 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-07-23 13:12:26 +02:00
DanB
d74784206b Removing CDR replication debug statements 2015-07-23 13:12:02 +02:00
DanB
b3cbf4eca2 Syncing data/conf/cgrates.json with internal defaults 2015-07-23 13:10:45 +02:00
Radu Ioan Fericean
eef3ab763b one more debug log removed 2015-07-23 11:43:48 +03:00
DanB
4b07c53ec6 Correct tests for Pdd in storage 2015-07-23 11:43:48 +03:00
DanB
bb526293e7 Make sure cdr_http field is replicable with cost and cgrid, fixes #124 2015-07-23 11:43:47 +03:00
DanB
b5479c387a Fixes in cdr_post link 2015-07-23 11:43:47 +03:00
DanB
b41c86e091 Add *http_post replication for CDRs, fixes #121 2015-07-23 11:43:46 +03:00
DanB
153360efd9 Adding initial alter_tables_rc5_rc6.sql, various test improvements 2015-07-23 11:43:46 +03:00
DanB
bbf4d03f85 Small fix import 2015-07-23 11:43:46 +03:00
DanB
9adb515fcb SM-FS: Disable request processing in case of process_cdr=false or dialplan=inline 2015-07-23 11:43:46 +03:00
Radu Ioan Fericean
951392cfa6 one more debug log removed 2015-07-23 11:43:15 +03:00
Radu Ioan Fericean
8abab18b07 added wait semaphore for response cache 2015-07-22 15:21:16 +03:00
Radu Ioan Fericean
9b1a4d14e5 unified response cache 2015-07-21 13:45:35 +03:00
DanB
33b718f664 Correct tests for Pdd in storage 2015-07-21 09:11:11 +02:00
DanB
0dc91e2981 Make sure cdr_http field is replicable with cost and cgrid, fixes #124 2015-07-20 21:42:24 +02:00
Radu Ioan Fericean
152debdc77 added response cache for loggcallcost 2015-07-20 12:23:09 +03:00
Radu Ioan Fericean
a7cf099c5c used response cache in responder 2015-07-20 12:15:24 +03:00
Radu Ioan Fericean
835f6533da added response cache mechanism 2015-07-20 09:56:20 +03:00
DanB
5bfa6e1ef8 Fixes in cdr_post link 2015-07-17 18:52:18 +02:00
Radu Ioan Fericean
7d302691b6 created connector pool 2015-07-17 19:27:53 +03:00
DanB
5c073f5164 Add *http_post replication for CDRs, fixes #121 2015-07-17 18:18:29 +02:00
DanB
700af0d72f Merge branch 'master' of https://github.com/cgrates/cgrates 2015-07-17 15:50:31 +02:00
DanB
17e469b615 Adding initial alter_tables_rc5_rc6.sql, various test improvements 2015-07-17 15:50:25 +02:00
Radu Ioan Fericean
99cf2cbd91 remove debug logging 2015-07-16 21:10:34 +03:00
Radu Ioan Fericean
4f142abc4b better logging just to make sure 2015-07-16 20:10:47 +03:00
Radu Ioan Fericean
cd8d42fc68 fix for duration index 2015-07-16 20:08:43 +03:00
Radu Ioan Fericean
e462f2051d fixes for refund operation 2015-07-16 19:47:43 +03:00
Radu Ioan Fericean
2ba3ce8ca3 fixing and logging 2015-07-16 19:42:51 +03:00
Radu Ioan Fericean
3e3861768e refund logging 2015-07-16 19:12:43 +03:00
Radu Ioan Fericean
60a3e1a9e3 logging all the way 2015-07-16 17:35:41 +03:00
DanB
86ec33f0a3 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-07-16 15:52:50 +02:00
Radu Ioan Fericean
994c3bf76a full loging for merged callcosts 2015-07-16 15:25:18 +03:00
Radu Ioan Fericean
2fb2e0d506 fixed local tests and added two new console cmds 2015-07-16 15:13:46 +03:00
DanB
8f18a0b2a0 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-07-16 13:14:45 +02:00
DanB
816d3aac64 Small fix import 2015-07-16 13:14:40 +02:00
DanB
5e8e9ef93f SM-FS: Disable request processing in case of process_cdr=false or dialplan=inline 2015-07-16 12:57:04 +02:00
Radu Ioan Fericean
d5ca85573f added call details logging 2015-07-16 10:58:13 +03:00
Radu Ioan Fericean
0b5591747f removed merge timespans check 2015-07-16 10:41:08 +03:00
Radu Ioan Fericean
39700e1c66 callcost merge simplified, possible fix for #118 2015-07-15 22:11:52 +03:00
DanB
986114be13 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-07-15 19:11:23 +02:00
DanB
87b9489d66 Disabling cdrc flatstore tests to make travis happy until we find a solution for the timestamp 2015-07-15 19:11:12 +02:00
Radu Ioan Fericean
50cafb92d7 added estra fields for user profile loading 2015-07-15 17:27:06 +03:00
Radu Ioan Fericean
e015ef9213 renamed tutorial users file 2015-07-15 15:50:18 +03:00
Radu Ioan Fericean
af15347b7f added user search ponders 2015-07-14 23:38:39 +03:00
Radu Ioan Fericean
c5b653ab63 moved LoadUserProfile to users and used reflect 2015-07-14 23:38:39 +03:00
Radu Ioan Fericean
e95b2be88d simpler console command interface 2015-07-14 23:38:39 +03:00
Radu Ioan Fericean
0f7cc579f3 added struct to map converters 2015-07-14 23:38:39 +03:00
Radu Ioan Fericean
30c327fc5f added stored cdr loader for user profile 2015-07-14 23:38:39 +03:00
DanB
b920693444 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-07-14 20:02:26 +02:00
DanB
56e7974bea Small config fixes, tests corrected 2015-07-14 20:02:12 +02:00
DanB
c15bed1938 Refactored partial records processing for flatstore cdrs 2015-07-14 19:49:07 +02:00
Radu Ioan Fericean
b215382e5b added test for remove account
fixes #116
2015-07-13 21:08:04 +03:00
Radu Ioan Fericean
dc821c59eb added account_remove console command 2015-07-13 20:48:34 +03:00
Radu Ioan Fericean
fd7beda6a9 added apier method 2015-07-13 20:41:24 +03:00
Radu Ioan Fericean
4954332087 added REMOVE_ACCOUNT action 2015-07-13 20:25:12 +03:00
Radu Ioan Fericean
1b5320da6f more tests for index handling 2015-07-13 18:23:01 +03:00
DanB
25d7ba9962 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-07-13 16:36:00 +02:00
DanB
8d7b5818f2 Init the partialRecords in cdrc 2015-07-13 16:35:47 +02:00
Radu Ioan Fericean
5c1c066996 removed extra logging message 2015-07-13 17:34:52 +03:00
Radu Ioan Fericean
3b14b399cb fixes for user indexes
tests pending
2015-07-13 17:33:00 +03:00
Radu Ioan Fericean
7b1b8cac63 better console commands and added user_indexes command 2015-07-13 17:33:00 +03:00
Radu Ioan Fericean
8dcd6b86e3 don't ask for oh-my-zsh upgrade' 2015-07-13 17:31:12 +03:00
DanB
d5f9ee9f56 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-07-13 16:13:59 +02:00
DanB
351c86f996 Fix for max_open_files in cdrc being 0 2015-07-13 16:13:47 +02:00
Dan Christian Bogos
364dd8d3c3 Merge pull request #117 from eloycoto/master
Storage: Added jsonb support in cost_details.timespans
2015-07-13 15:21:49 +02:00
Eloy Coto
5569e103de Storage: Added jsonb support in cost_details.timespans 2015-07-13 09:41:31 +01:00
DanB
b487bbed36 Initial flatstore local test 2015-07-12 21:48:33 +02:00
DanB
61344c1dbf cdrc.dumpUnpairedRecords mecanism to auto-clean cache of the partial flatstore CDR files 2015-07-12 15:58:21 +02:00
DanB
7cd65d3fe9 Flatstore CDRs supporting failed/missed calls CDRs 2015-07-12 15:19:51 +02:00
DanB
0351664758 Add NewGuardianConstructor, rename accountlock.go->guardian.go files, adding lock on caching the partial flatstore records 2015-07-12 13:44:06 +02:00
DanB
a4824c5c86 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-07-12 13:08:47 +02:00
DanB
304538f02e Complete test for Flatstore cdrc imports 2015-07-12 12:59:34 +02:00
Radu Ioan Fericean
ce2bcac0b9 more refactoring on Guardian 2015-07-10 23:33:45 +03:00
Radu Ioan Fericean
0dae4eab28 Merge branch 'users' 2015-07-10 23:30:54 +03:00
Radu Ioan Fericean
e89c06952e renamed AccLock to Guardian 2015-07-10 23:28:53 +03:00
Radu Ioan Fericean
87d8dee93a added user service console commands 2015-07-10 23:23:56 +03:00
DanB
c359516996 Partial implementation of db_flatstore CDRs from *ser 2015-07-10 21:03:38 +02:00
Radu Ioan Fericean
8f7e3efd61 refactored server starting and added configs 2015-07-10 19:01:08 +03:00
DanB
c78953a19f Fix for smsUsageMultiplyFactor being applied for cost multiply 2015-07-09 12:59:28 +02:00
Radu Ioan Fericean
1bbe6c10c6 added user saving 2015-07-08 22:36:54 +03:00
Radu Ioan Fericean
8668df8d49 added loaders for user data 2015-07-08 20:08:04 +03:00
Dan Christian Bogos
7beefd49fe Merge pull request #113 from eloycoto/master
Storage: Added jsonb support in cdrs_extra.extra_fields
2015-07-08 19:01:47 +02:00
DanB
25df2b85a6 Small fix indexing of the stats 2015-07-08 19:01:14 +02:00
DanB
cc762e9551 Renaming tp_cdrstats -> tp_cdr_stats in storage models 2015-07-08 18:50:17 +02:00
Eloy Coto
f4e1eb9f7a Storage: Added jsonb support in cdrs_extra.extra_fields 2015-07-08 17:10:59 +01:00
Radu Ioan Fericean
7acf368bb2 added index and tests 2015-07-08 15:41:08 +03:00
Radu Ioan Fericean
ee822d4acc added tests before indexes 2015-07-08 14:15:39 +03:00
DanB
8fb98ef1d2 Add MaxOpenFiles configuration option for CDRC, fixes #111 2015-07-08 13:02:16 +02:00
DanB
840c46ef1a Populate Subject with Account information when missing, fixes #78 2015-07-08 11:37:26 +02:00
DanB
511a83cda1 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-07-07 19:45:45 +02:00
DanB
e110b00c6c Make sure we produce the same CDR from both channel_hangup_complete and json CDR, closes #92 2015-07-07 19:45:32 +02:00
Radu Ioan Fericean
97fbac6a68 started users service 2015-07-07 19:49:11 +03:00
Radu Ioan Fericean
47387150af renamed account console command to accounts 2015-07-06 21:13:30 +03:00
Radu Ioan Fericean
c5b321a3be enhanced account console command 2015-07-06 21:13:30 +03:00
DanB
5aa5684aa0 Renaming rating and accounting db also in the sources 2015-07-06 18:46:31 +02:00
DanB
91a16f436b Configuration database name changes rating_db -> tariffplan_db, accounting_db -> data_db, fixes #109 2015-07-06 18:13:30 +02:00
DanB
03c56146dc Merge branch 'master' of https://github.com/cgrates/cgrates 2015-07-06 12:49:18 +02:00
Radu Ioan Fericean
3110c0771f fix for local tests 2015-07-06 13:45:28 +03:00
DanB
a2373bb84d Merge branch 'master' of https://github.com/cgrates/cgrates 2015-07-06 11:34:57 +02:00
Radu Ioan Fericean
7d90cf3e85 added DDC - Destination Distinct Count
fixes #106
2015-07-06 12:33:17 +03:00
DanB
e0888f72d7 Adding Overwrite parameter to ApierV1.SetDerivedChargers, tests for ApierV1.SetDestination 2015-07-06 11:29:09 +02:00
Radu Ioan Fericean
10ef469fdb added API and console command for destination set
fixes #106
2015-07-06 10:07:33 +03:00
Radu Ioan Fericean
95b5373ab2 fixes 2015-07-03 20:28:41 +03:00
Radu Ioan Fericean
d405c81166 started Destination Id Archive 2015-07-03 20:21:40 +03:00
Radu Ioan Fericean
a3e2526c42 published first event 2015-07-03 20:21:07 +03:00
Radu Ioan Fericean
5adc94a45f added cdrstats_queue and cdrstats_queue_triggers
console commands fixes #107
2015-07-03 17:26:25 +03:00
Radu Ioan Fericean
dbdc95422f removed duplicate update command 2015-07-03 15:10:49 +03:00
Dan Christian Bogos
c0763a3d14 Merge pull request #105 from eloycoto/master
Doc: Added some changes
2015-07-03 12:15:10 +02:00
Eloy Coto
a7a8c397f4 Doc:
- Add some tarriff_plans fixes and added cdr_stats
    - Extend cdrstats documentation + provide examples
2015-07-03 10:35:38 +01:00
DanB
4277f682c7 Remove log out of test since it is making the test failing with nil pointer 2015-07-02 19:14:25 +02:00
DanB
4725e5dfe9 CgrEvent PassFilters implementation 2015-07-02 18:54:09 +02:00
Radu Ioan Fericean
dd80669439 compare Timing with time in UTC 2015-07-02 18:40:07 +03:00
DanB
8fa60bed50 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-07-02 16:36:03 +02:00
DanB
0b3cf7db1a Adding eamon special timestamp format 2015-07-02 16:35:58 +02:00
Radu Ioan Fericean
e843d27c70 using rsr filters to match events 2015-07-02 15:32:37 +03:00
DanB
b66a8e6b44 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-07-02 11:40:36 +02:00
DanB
5e08c671ce Build CgrId automatically for externalCdr when missing, more tests on local 2015-07-02 11:40:31 +02:00
Radu Ioan Fericean
897c6fddf6 added filters in subscriber data 2015-07-02 11:49:17 +03:00
Radu Ioan Fericean
98d416b91c removed pubsub dir from tests 2015-07-02 10:21:38 +03:00
Radu Ioan Fericean
fc3c7a31ee Merge branch 'pubsub' 2015-07-02 10:18:08 +03:00
Radu Ioan Fericean
567b1e77dd json rpc for cgr tester 2015-07-02 10:18:00 +03:00
Radu Ioan Fericean
285936f02b added tests for pubusub save function 2015-07-02 10:15:42 +03:00
Radu Ioan Fericean
54fa1476d9 added console commands and ShowSubscribers method 2015-07-02 09:59:26 +03:00
Radu Ioan Fericean
9c9465e1fc removed pubsub save interval 2015-07-02 09:16:45 +03:00
Radu Ioan Fericean
342415a6ad removed logging 2015-07-01 22:15:38 +03:00
Radu Ioan Fericean
1c809bb297 Merge branch 'master' into pubsub 2015-07-01 22:14:07 +03:00
Radu Ioan Fericean
1fb051ef7a refactoring and saving subscribers 2015-07-01 22:12:22 +03:00
DanB
1659ba8576 Adding ApierV1.DebitUsage API file 2015-07-01 20:11:52 +02:00
DanB
309ee81db7 Adding test for ApierV1.MaxUsage, fix Responder.GetDerivedMaxSessionTime to consider usage of the event 2015-07-01 20:09:19 +02:00
Radu Ioan Fericean
e7f0b617ec first successful live test 2015-07-01 20:34:32 +03:00
Radu Ioan Fericean
02dc95d815 added pubsub configs 2015-07-01 19:25:47 +03:00
Radu Ioan Fericean
a3affa32b1 json rpc for cgr tester 2015-07-01 16:47:54 +03:00
DanB
1135dc07e0 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-07-01 13:47:31 +02:00
DanB
2659742310 ApierV1.DebitUsage method, rename ApierV1.GetMaxSessionTime->ApierV1.GetMaxUsage 2015-07-01 13:47:18 +02:00
Radu Ioan Fericean
66ac4b194c added transport and sync locks 2015-07-01 12:03:15 +03:00
Radu Ioan Fericean
173b025d8f first pubsub tests 2015-06-30 22:54:06 +03:00
Radu Ioan Fericean
e99b3ff16c pubsub first draft 2015-06-30 19:09:15 +03:00
Radu Ioan Fericean
aa10ac0930 no trigger run on load 2015-06-30 15:19:28 +03:00
Radu Ioan Fericean
be59d92ee4 reverted to good add queue version 2015-06-30 14:04:46 +03:00
DanB
137bd4a8d5 Removing .Debug for stats 2015-06-30 12:44:57 +02:00
DanB
e0d1505fd3 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-06-30 12:33:24 +02:00
DanB
e4c3d46955 Some debug in stats 2015-06-30 12:33:20 +02:00
Radu Ioan Fericean
afa104f369 call stats stop on kill and better add queue 2015-06-30 13:24:01 +03:00
DanB
8e1195b418 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-06-30 10:52:16 +02:00
DanB
f744967c5f Small fix stats_test 2015-06-30 10:52:13 +02:00
Radu Ioan Fericean
7e5643347d only saving queued cdrs 2015-06-30 11:45:37 +03:00
DanB
f055b08d5f Merge fix 2015-06-30 10:20:37 +02:00
DanB
3d745e4a51 Removing *default stats building 2015-06-30 10:16:52 +02:00
Radu Ioan Fericean
cf7b11e765 cleanner queue saver 2015-06-29 23:29:25 +03:00
DanB
7055573f4c Local test for offline cdrstats persistence 2015-06-29 15:12:04 +02:00
DanB
1abd6d526e Default Stats save_interval updated to 1m 2015-06-29 14:59:05 +02:00
Radu Ioan Fericean
a9d5918514 fixes for cdr stats saving/loading 2015-06-29 13:32:32 +03:00
Radu Ioan Fericean
68ce365435 removed debug log line 2015-06-26 21:47:03 +03:00
Radu Ioan Fericean
3a637d12f4 removed cdrs debug logging 2015-06-24 11:24:05 +03:00
Radu Ioan Fericean
b32e441505 better stats shutdown 2015-06-23 17:46:09 +03:00
Radu Ioan Fericean
2b4559f8c3 update action triggers without reseting metric values
reference to #90
2015-06-23 17:00:04 +03:00
Radu Ioan Fericean
e7a2a048db Merge branch 'stats_save' 2015-06-22 23:22:45 +03:00
Radu Ioan Fericean
10c13d0694 update data files and test fixes 2015-06-22 23:21:36 +03:00
Radu Ioan Fericean
ef5413118f first draft of cdr stats queues save/load 2015-06-19 22:55:27 +03:00
Radu Ioan Fericean
abdb6ff1f6 better debug logging 2015-06-19 09:28:53 +03:00
Radu Ioan Fericean
3d0553ac37 started stats saver 2015-06-18 23:26:19 +03:00
Radu Ioan Fericean
1ae2020c0f some debugging info 2015-06-18 19:40:21 +03:00
DanB
1e006c3ad3 Fix guard 2015-06-18 18:15:03 +02:00
DanB
97805baa0b Adding guard back to CDRS 2015-06-18 18:10:55 +02:00
DanB
d7033c8dda Sync 2015-06-18 17:15:41 +02:00
DanB
9b8dcd4ffd Remove .Debug statements from CDRS 2015-06-18 16:59:11 +02:00
DanB
0b9753ad79 CDRS to only process rawCdr synchronously and all the rest async to speed up the process and avoid struct locks - if any 2015-06-18 16:57:15 +02:00
Radu Ioan Fericean
14a225e269 possible fix for #99 2015-06-18 17:14:30 +03:00
Radu Ioan Fericean
8f8da4af36 improvements on negative charging 2015-06-18 16:57:37 +03:00
Radu Ioan Fericean
be99e08cd2 guarding fix 2015-06-18 15:40:31 +03:00
Radu Ioan Fericean
908ced9b2d the real guardian of duplicate call info log 2015-06-18 15:08:27 +03:00
Radu Ioan Fericean
ecc7af8acb guard call cost log duplicate check 2015-06-18 14:43:27 +03:00
Radu Ioan Fericean
81c7593726 created test for negative debit 2015-06-17 21:57:12 +03:00
Radu Ioan Fericean
77d2048cab better rating info 2015-06-17 21:57:12 +03:00
DanB
4fdc4bfd87 Remove .Debug messages in CDRS 2015-06-17 19:45:45 +02:00
DanB
3d8be3c60b Adding async processing to the CDRs out of http interface, lock checkDuplicate in CDRS 2015-06-17 19:34:41 +02:00
DanB
07f43532ed Delay without fib() 2015-06-17 18:45:18 +02:00
DanB
2c7bad2230 Adding some more debug 2015-06-17 18:06:30 +02:00
DanB
89149ecf0b LogCallCost should check error by type and not message 2015-06-17 17:41:10 +02:00
DanB
237f5ef6e4 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-06-17 16:39:02 +02:00
DanB
dbf88f0896 Adding debug info for #97 2015-06-17 16:38:29 +02:00
Radu Ioan Fericean
bb52349e8f rating info for *zero based balances (#51, #96) 2015-06-17 16:58:02 +03:00
DanB
5341fd07c8 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-06-17 14:29:52 +02:00
DanB
dbf49f2aae Add prepaid to getCostFromRater for recalculation when no costs from session manager 2015-06-17 14:26:58 +02:00
Radu Ioan Fericean
ece34c4b11 fix for local tests and better cdrstats loading 2015-06-17 14:21:13 +03:00
DanB
7fd136eaf2 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-06-17 11:59:45 +02:00
DanB
ca19583f81 Small CDRS fix, reported issue in #97, adding free minutes example in tutorial TP, adding some more tests in fs tutorial 2015-06-17 11:59:32 +02:00
Radu Ioan Fericean
896a925d25 merged 2015-06-16 22:14:56 +03:00
Radu Ioan Fericean
762d759d4e test fixes 2015-06-16 22:09:10 +03:00
Radu Ioan Fericean
e802e1eee7 fixed more local tests 2015-06-16 22:09:10 +03:00
Radu Ioan Fericean
fcb5fee2fc normal test passing 2015-06-16 22:09:10 +03:00
Radu Ioan Fericean
1ac33f5b20 test fixes 2015-06-16 21:51:56 +03:00
Radu Ioan Fericean
623adf919a fixed more local tests 2015-06-16 21:27:42 +03:00
Radu Ioan Fericean
557d1de4b2 normal test passing 2015-06-16 19:45:01 +03:00
DanB
61d9919df6 Simplified rateCdr in CDRS 2015-06-16 17:26:14 +02:00
DanB
d24c5a1872 Ident simplify in syncactivecalls of SM 2015-06-15 19:36:36 +02:00
DanB
4e7f5fd364 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-06-15 19:31:24 +02:00
DanB
9e0b234d55 Active synchronization for channels between CGR and FreeSWITCH, adding new channel_sync_interval configuration in session manager, should fix and close #77 2015-06-15 19:31:19 +02:00
Radu Ioan Fericean
dd08922538 fix for #88
shared groups werw not writtent to redis
2015-06-15 20:18:35 +03:00
Radu Ioan Fericean
af787149e2 fix for redis password issue 2015-06-15 19:08:04 +03:00
DanB
d13040a9f1 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-06-15 10:54:33 +02:00
DanB
aa84cf23cd ActiveSession in sessionmanager 2015-06-15 10:54:30 +02:00
Radu Ioan Fericean
910813f4ad compilation fix 2015-06-15 11:41:24 +03:00
Radu Ioan Fericean
0168cd3cf7 fix for console josn Marshall issue 2015-06-15 11:29:35 +03:00
DanB
92fd6be01d Console active_sessions command and missing smv1.go file 2015-06-14 21:25:52 +02:00
DanB
a33422f224 Initial SessionManagerV1 API methods 2015-06-14 21:24:49 +02:00
DanB
2d440a046d FreeSWITCH cgr_computelcr channel variable processing, fix logging of callcosts from session manager, improved tutorial_fs testing 2015-06-14 19:02:58 +02:00
DanB
4957ba1a4a Adding Unscoped parameter to CdrsFilter to marking that results should search in soft-deletes also 2015-06-14 16:18:50 +02:00
Radu Ioan Fericean
9ffc2a4715 refactored errors 2015-06-13 16:45:09 +03:00
Radu Ioan Fericean
6ed59dafe9 moved derived charger with passing tests 2015-06-12 21:19:37 +03:00
Radu Ioan Fericean
b02843d324 Merge branch 'master' into dcinrt 2015-06-12 13:42:39 +03:00
Radu Ioan Fericean
ac228a6bc5 added test for shared groups set/get 2015-06-12 13:21:48 +03:00
Radu Ioan Fericean
e7224a4a39 started derived chargers move, tests failing 2015-06-12 12:38:23 +03:00
DanB
b6a4239dad Merge branch 'master' of https://github.com/cgrates/cgrates 2015-06-12 10:42:47 +02:00
DanB
276695b37d Processing cgr_ignorepark channel variable for SM-FreeSWITCH 2015-06-12 10:42:32 +02:00
DanB
1dfc6c5b3e Fix nil pointer error when no database connection on dry_run 2015-06-12 10:28:53 +02:00
Radu Ioan Fericean
88078e34be deep parameter help for console 2015-06-12 10:42:40 +03:00
DanB
b984360548 Adding subscribe_park configuration in sm-freeswitch 2015-06-11 19:15:04 +02:00
Radu Ioan Fericean
e63689fed1 using new *any and *asap in tests 2015-06-11 18:32:29 +03:00
Radu Ioan Fericean
f401947af1 added default *asap timing tag 2015-06-11 15:33:54 +03:00
Radu Ioan Fericean
eb759d8243 added *generic type debit test 2015-06-11 15:23:07 +03:00
Radu Ioan Fericean
c230169b2a added *generic type of record 2015-06-11 14:52:24 +03:00
Radu Ioan Fericean
f13cdab64d test for *any timing 2015-06-11 13:58:06 +03:00
Radu Ioan Fericean
8bfa0997bd added *any timing tag 2015-06-11 11:51:48 +03:00
Radu Ioan Fericean
ba9541aef0 added one more refund test 2015-06-11 10:07:34 +03:00
Radu Ioan Fericean
c732a18ae9 more checks for tags existance 2015-06-10 20:49:19 +03:00
Radu Ioan Fericean
5903b40e83 Merge branch 'session' 2015-06-10 14:43:59 +03:00
Radu Ioan Fericean
25e7bdb1c6 updated fsnotify to the new location and latest version 2015-06-10 14:43:57 +03:00
Radu Ioan Fericean
78764e9979 refund tests 2015-06-10 14:40:20 +03:00
Radu Ioan Fericean
677ca7e037 Merge branch 'master' into session 2015-06-09 22:12:33 +03:00
Radu Ioan Fericean
e0e89ef852 check for callcost log existance 2015-06-09 22:12:21 +03:00
Radu Ioan Fericean
73d6a858c6 updated fsnotify to the new location and latest version 2015-06-09 21:17:18 +03:00
Radu Ioan Fericean
56986230ad refund everything on duplicate callcost log 2015-06-09 21:02:08 +03:00
DanB
d4b8bb96d2 CDRC to support supplier, disconnect_cause and pdd as primary fields 2015-06-09 19:19:26 +02:00
Radu Ioan Fericean
c7e0a3943b Merge branch 'master' into session 2015-06-09 20:18:01 +03:00
DanB
495bb1ba0c Merge branch 'master' of https://github.com/cgrates/cgrates 2015-06-09 16:55:12 +02:00
DanB
f47d3647cd Small fix default config 2015-06-09 16:54:57 +02:00
Radu Ioan Fericean
bf6a5537de updated pq location 2015-06-09 15:45:30 +03:00
DanB
8277d0b789 Another comment removed out of default cgrates.json config 2015-06-09 14:03:30 +02:00
DanB
9aea8f2abb Config defaults fix to remove extra comment on copyright, default of cdrs is to connect to rater 2015-06-09 14:00:28 +02:00
Radu Ioan Fericean
ab9280dbe5 first draft of stale sessions protection 2015-06-09 14:37:48 +03:00
DanB
18c76c212b Adding create_cdr setting in tutorial configs 2015-06-09 12:43:47 +02:00
DanB
471736fcda Config defaults updated, cdrs=internal on SM components, sanity checks updated 2015-06-09 12:33:09 +02:00
DanB
8b0811267e Adding CreateCdr parameter to session manager 2015-06-09 12:23:15 +02:00
Radu Ioan Fericean
f454707445 Merge branch 'session' 2015-06-09 11:46:17 +03:00
Radu Ioan Fericean
afea97d0b5 use cdrs for callcost logging 2015-06-09 11:45:30 +03:00
DanB
e8a08903ee PDD in CDRS, small fixes in storedcdr 2015-06-08 18:47:53 +02:00
DanB
45275f08f7 Adding Pdd to derived charging 2015-06-08 18:33:56 +02:00
DanB
175d73633c Removing *default cdrstats out of .json configuration since the same functionality can be achieved over .csv load 2015-06-08 16:53:13 +02:00
DanB
fcd5a13ccd Merge branch 'master' of https://github.com/cgrates/cgrates 2015-06-08 16:16:30 +02:00
DanB
aa4cd62a7f Adding PddInterval in CdrStats 2015-06-08 16:16:22 +02:00
DanB
05a522a64c Modified order of fields in stordb to include pdd after setuptime, small apier test fixes 2015-06-08 12:31:26 +02:00
Radu Ioan Fericean
cfc766677a better qos sorter 2015-06-08 12:56:42 +03:00
Radu Ioan Fericean
5bfb6da295 CdrStats name change
# Changes to be committed:
2015-06-08 11:52:26 +03:00
Radu Ioan Fericean
e4c5b8a577 Merge branch 'master' into load 2015-06-08 11:34:47 +03:00
DanB
964a5c07d3 Adding PDD to LCR/QOS 2015-06-08 10:20:13 +02:00
Radu Ioan Fericean
2de5b4811c better validation and names fixes 2015-06-08 11:06:14 +03:00
DanB
0046a1bedd Reverting to cdrstats instead of cdr_stats for subsystems naming consistency 2015-06-07 11:56:02 +02:00
DanB
160e6f5421 PddInterval in CdrStats.AcceptCdr 2015-06-07 09:50:28 +02:00
Radu Ioan Fericean
d3ec5c27e6 added existing regular expressions 2015-06-06 14:36:07 +03:00
Radu Ioan Fericean
f9012a01a9 utils csv export tests move toe model helpers 2015-06-06 13:14:55 +03:00
Radu Ioan Fericean
d0a121ca05 fix for apier local tests 2015-06-06 10:03:10 +03:00
Radu Ioan Fericean
2312bedfe7 Merge branch 'master' into load 2015-06-06 09:49:57 +03:00
Radu Ioan Fericean
49d4d5f591 general tests passing 2015-06-06 00:30:47 +03:00
Radu Ioan Fericean
f1da3ed1eb local engine tests passing 2015-06-06 00:01:54 +03:00
DanB
16793018a2 Adding Min/MaxPdd as CdrsFilter, renamed UsageStart->MinUsage, UsageEnd->MaxUsage, CostStart->MinCost, CostEnd->MaxCost for better readability, added sql tests for Min/MaxPdd filter 2015-06-05 20:46:09 +02:00
Radu Ioan Fericean
869168a73d more test fixes 2015-06-05 20:12:05 +03:00
DanB
b27daa074b Adding PDD to storDb, renaming CdrsFilter.FilterOnDerived -> CdrsFilter.FilterOnRated, added storDb store-restore CDR tests 2015-06-05 18:56:55 +02:00
Radu Ioan Fericean
72ac8052bf buils script finishing succesfuly 2015-06-05 16:18:50 +03:00
Radu Ioan Fericean
dd7fe52ce6 apier compiling and passing tests 2015-06-05 16:00:07 +03:00
DanB
200e419dcd Adding Pdd inside StorDb and events 2015-06-05 14:56:39 +02:00
Radu Ioan Fericean
29f51cd478 normal engine tests passing 2015-06-05 10:46:39 +03:00
Radu Ioan Fericean
d74b772007 more test fixes and renamed internal ActionPlan 2015-06-04 14:58:32 +03:00
Radu Ioan Fericean
32ef1d1ba3 some test fixes 2015-06-04 00:52:11 +03:00
Radu Ioan Fericean
ebbb8d6f6b Merge branch 'master' into load 2015-06-03 21:49:12 +03:00
Radu Ioan Fericean
cacb13b4ac added model converters and fixed test compilation
engine test now compile need data fixes
2015-06-03 21:39:47 +03:00
Radu Ioan Fericean
e75f05e0d3 apier compilation fixes 2015-06-02 23:44:22 +03:00
Radu Ioan Fericean
9c66af024d compilation fixes 2015-06-02 23:29:09 +03:00
Radu Ioan Fericean
7330061c1f refactored LoadWriter, tpexporter and tpimporter 2015-06-01 23:21:00 +03:00
DanB
a203bbfcba Merge branch 'master' of https://github.com/cgrates/cgrates 2015-06-01 20:48:31 +02:00
DanB
1bac65a9e0 Adding cdrc_fs.json in the FS tutorial, various kamailio tutorial improvements 2015-06-01 20:48:23 +02:00
Radu Ioan Fericean
3ce3084962 return the csv error instead of continue 2015-06-01 17:13:42 +03:00
Radu Ioan Fericean
9676748ac4 print bad lines in csv 2015-06-01 17:09:00 +03:00
Radu Ioan Fericean
1b9b47ceb4 added indexes to struct fields 2015-05-29 16:28:23 +03:00
Radu Ioan Fericean
a0d0424f79 added csv dump
from tagged struct to csv string
2015-05-29 13:19:33 +03:00
Radu Ioan Fericean
7ec5c06747 Merge branch 'master' into load 2015-05-28 20:59:12 +03:00
Radu Ioan Fericean
29b83ee26c removed analytics pixel 2015-05-28 20:59:02 +03:00
Radu Ioan Fericean
0fd480e5c1 added accounting loaders 2015-05-28 20:48:19 +03:00
Radu Ioan Fericean
99a244060e unified tp_reader and tp_data files 2015-05-27 20:04:03 +03:00
Radu Ioan Fericean
4c3d6919f8 unifying csv and db loaders, work in progress 2015-05-27 15:12:12 +03:00
Radu Ioan Fericean
445fd02640 Merge branch 'master' into load 2015-05-26 20:56:08 +03:00
Radu Ioan Fericean
3f3cb5b3ce fixes for local tests 2015-05-26 20:55:19 +03:00
Radu Ioan Fericean
34b0e86811 better destinations helper 2015-05-26 20:35:18 +03:00
Radu Ioan Fericean
c68a660374 created reflection csv loader 2015-05-26 20:22:08 +03:00
DanB
a2e36b6ae6 Adding disconnect cause in kamailio SM, modified tests 2015-05-26 19:09:20 +02:00
Radu Ioan Fericean
f4f56937b7 fix for maxcost callcost information 2015-05-26 15:24:36 +03:00
Radu Ioan Fericean
0d31aba1dd Merge branch 'master' into load 2015-05-26 13:58:05 +03:00
Radu Ioan Fericean
844840e184 fix for maxcost callcost information 2015-05-26 13:50:54 +03:00
DanB
d97b996739 Adding template files for FreeSWITCH default .csv format 2015-05-26 12:50:36 +02:00
Radu Ioan Fericean
97025381b3 test for GetCost with maxcost enabled 2015-05-26 12:30:17 +03:00
DanB
636574cc5a Merge branch 'master' of https://github.com/cgrates/cgrates 2015-05-26 11:21:28 +02:00
DanB
ec60709daa Adding MaxCallCost related tests in local 2015-05-26 11:21:19 +02:00
Radu Ioan Fericean
eeadc8a55e founding for the new call cost 2015-05-26 10:21:53 +03:00
Radu Ioan Fericean
eb8df9892a getcost uses maxcost strategy 2015-05-25 20:16:36 +03:00
DanB
cf9539f2bd Adding ToJSON method in callcost, tests for MaxCallCost 2015-05-25 17:07:05 +02:00
DanB
77a9709b27 Fix loader to accept *disconnect as strategy in MaxCallCost, adding MaxCallCost in tutorial TP 2015-05-25 16:23:11 +02:00
DanB
23c5313d6c Adding tutlocal sample config 2015-05-25 15:35:38 +02:00
DanB
b5d972a930 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-05-25 15:32:08 +02:00
Radu Ioan Fericean
9c0d85ffd3 Merge branch 'master' into load 2015-05-25 16:31:18 +03:00
Radu Ioan Fericean
36595d88f6 fix for cdrlog action 2015-05-25 16:30:40 +03:00
DanB
800f9555da CdrStats in tutorial_local_test 2015-05-25 15:09:20 +02:00
DanB
bb97e626ab Check CDRStats in kamailio tutorial 2015-05-25 14:55:24 +02:00
Radu Ioan Fericean
ccd91e36ae started loader refactoring 2015-05-25 14:40:41 +03:00
DanB
fdef1bfe67 Adding QOS to LcrReply, Error as string in supplierCost 2015-05-25 13:18:47 +02:00
DanB
060beeb182 Complete test suite for LCR strategies in Tutorial 2015-05-25 12:17:33 +02:00
DanB
2776c66b57 Small test fix 2015-05-24 20:52:31 +02:00
DanB
a81bf953b7 Tutorial LcrRules.csv fixups, adding TestTutLocalLcrStatic 2015-05-24 20:50:26 +02:00
DanB
83bab44b3c LcgProfiles added in cache stats 2015-05-24 18:32:46 +02:00
DanB
73be367dd7 Better LCR error handling, SM-Kamailio fixes for LCR handling 2015-05-24 17:43:37 +02:00
DanB
dbd0f369b1 Adding LCR processing to SM-Kamailio 2015-05-23 21:02:05 +02:00
Radu Ioan Fericean
61b342ddce return erro on disabled lcr supplier 2015-05-22 17:16:02 +03:00
Radu Ioan Fericean
364f74c6c8 more validations and tests 2015-05-21 16:38:22 +03:00
Radu Ioan Fericean
bb6a7ea6c3 timings and rate groups validation 2015-05-21 15:41:05 +03:00
Radu Ioan Fericean
3a0fe8e67c more duplicate code removal 2015-05-21 11:53:39 +03:00
Radu Ioan Fericean
013176053b refactored loaders to remove code duplication 2015-05-21 11:24:03 +03:00
Radu Ioan Fericean
e7b47e760f add docker starter script and fixes for rating plan validation 2015-05-21 10:44:19 +03:00
Radu Ioan Fericean
d46ade3403 started rating profiles data validation on load 2015-05-21 10:19:51 +03:00
Radu Ioan Fericean
554d84266f higher weights are better 2015-05-20 14:12:21 +03:00
Radu Ioan Fericean
253d3e4f0a return error on missing rate intervals 2015-05-20 14:12:21 +03:00
Radu Ioan Fericean
da7e2580b7 added holiday rating plan test 2015-05-20 14:12:21 +03:00
DanB
3043065267 Config sanity checks added 2015-05-16 17:33:00 +02:00
Radu Ioan Fericean
d2b4df69ca Balance DestinationId now take multiple destinations 2015-05-16 00:01:30 +03:00
Radu Ioan Fericean
1e5e025cc3 use Id instead of ID 2015-05-15 14:47:16 +03:00
DanB
d59468f7c8 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-05-15 09:44:33 +02:00
DanB
537c8ec558 DisconnectCause in CdrStat filters 2015-05-15 09:44:14 +02:00
Radu Ioan Fericean
3cfeaf7ac5 add rating plan id to call cost 2015-05-14 11:06:25 +03:00
DanB
24edc024f7 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-05-13 23:42:57 +02:00
DanB
3bc9f0e4cd DisconnecCause in StoredCdr and DerivedCharging 2015-05-13 23:42:29 +02:00
Radu Ioan Fericean
91d29f8135 units debit missing rateinterval protection 2015-05-13 13:31:01 +03:00
Radu Ioan Fericean
6d2e84bd36 fix tutorial offpeak timings 2015-05-13 13:26:49 +03:00
Radu Ioan Fericean
e92ac249b2 run GOPATH and GOROOT binaries first 2015-05-13 13:16:13 +03:00
Radu Ioan Fericean
de75662a1d put the nil ratinginfo protection back 2015-05-12 22:40:34 +03:00
Radu Ioan Fericean
35d75eba8d added category for maxduration console command 2015-05-12 22:40:34 +03:00
DanB
97e85f151a Increase debit interval on OpenSIPS tutorial 2015-05-12 16:41:20 +02:00
DanB
3f397f1954 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-05-12 10:23:25 +02:00
DanB
6d6f598827 Adding LCR support to OpenSIPS integration 2015-05-12 10:23:19 +02:00
Radu Ioan Fericean
73b587e3a0 remove nil protection for later testing 2015-05-12 10:55:20 +03:00
Radu Ioan Fericean
63bcc21a46 protection against nil ratinginfo 2015-05-12 07:45:54 +03:00
Radu Ioan Fericean
5ec5fa2064 no start command 2015-05-11 22:49:01 +03:00
Radu Ioan Fericean
8a97b11f8e opensips doker file with all service working 2015-05-11 22:43:42 +03:00
DanB
41732dfe1d Import fix 2015-05-11 09:15:51 +02:00
DanB
dcc474646f Completing opensips_async tutorial, remove debug info in sessionmanager 2015-05-11 08:59:34 +02:00
DanB
2b362b3a4d Adding tutorial_osips_calls file, removing obsolete data/tutorials/osips_async, optimizing kam_calls test file 2015-05-11 08:31:11 +02:00
DanB
242b6230f1 Cleanup opensipsm with comments, fix opensipsevent_test for GetUUID not anymore considering from and to tags 2015-05-10 19:49:28 +02:00
DanB
5043de5d17 Completing the SM-OpenSIPS component based on E_ACC_EVENT and E_ACC_MISSED_EVENT with automatic CDR generation 2015-05-10 19:31:08 +02:00
DanB
57db2b44e1 Better send commands for SM-OpenSIPS component 2015-05-10 13:33:27 +02:00
DanB
848fc37281 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-05-09 19:58:05 +02:00
DanB
7fc8914d4d Small fix mandatory parameters missing test for opensips event 2015-05-09 19:57:52 +02:00
DanB
df65bebd6a Partial session manager implementation for opensips 2015-05-09 19:56:45 +02:00
Radu Ioan Fericean
0558ca8272 added flycheck little refactorings 2015-05-09 13:36:46 +03:00
DanB
6b3c5430a3 OpenSIPS-SM with CDR and emulated dialog_start event 2015-05-09 12:26:34 +02:00
Radu Ioan Fericean
5ac7c4508e refactored var name 2015-05-09 13:16:33 +03:00
Radu Ioan Fericean
023ee2f08a more docker improvements 2015-05-09 11:42:39 +03:00
Radu Ioan Fericean
de12564377 better Dockerfile 2015-05-09 11:34:15 +03:00
DanB
5a2c195b3b Core dump folder for opensips init script 2015-05-09 10:03:31 +02:00
DanB
21c1dfcd79 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-05-07 11:30:13 +02:00
DanB
a826604eca MaxUsageReq to be used in callsetup APIs, OpenSIPS-SM modifications for auth, opensips.cfg changes in tutorial, adding *now in ParseTimeDetectLayout function 2015-05-07 11:30:02 +02:00
DanB
9346a52025 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-05-05 19:13:31 +02:00
DanB
3adf5809c0 ApierV2.ExportCdrsToFile method 2015-05-05 19:13:14 +02:00
Radu Ioan Fericean
0cafc4b297 imrpoved prod docker image too (not yet usable) 2015-05-05 19:28:47 +03:00
Radu Ioan Fericean
21853f2bb7 working devel docker image 2015-05-05 18:43:45 +03:00
DanB
dc09a4fefc Adding extra test for getCost 0 duration 2015-05-04 11:30:30 +02:00
Dan Christian Bogos
0684c3751d Merge pull request #65 from eloycoto/master
Update docs
2015-05-04 11:15:29 +02:00
Eloy Coto
e7be10283b Doc: update Actions section in tarifplans 2015-05-04 10:02:18 +01:00
Eloy Coto
f1b0ed2caf Doc: Update tables from tutorial ratingplans 2015-05-04 09:42:14 +01:00
Radu Ioan Fericean
7b554b0c6d fix for zero duration calldescriptors 2015-05-04 11:24:22 +03:00
Eloy Coto
a518ee769a Kamailio:
- Missed calls configuration
    - Add evapi listen address
2015-05-04 09:12:34 +01:00
Eloy Coto
72f39254f0 Mail conf need the server port, if not TCP dial will fail. 2015-05-04 09:01:27 +01:00
DanB
dbfa50d8ea Adding zero duration callcost test 2015-05-02 18:39:18 +02:00
DanB
c005d4f195 Tuneups OpenSIPS-SM, adding data/tutorials/osips_async, config updated with defaults 2015-05-02 18:37:17 +02:00
DanB
70f5f90e0b Lcr console command 2015-05-02 13:54:10 +02:00
DanB
ac1b3fdf6e Small import fix 2015-05-02 10:58:47 +02:00
DanB
c38c25d39c Do not add supplier to LCR if we cannot calculate his cost, removing some unused debug 2015-05-02 10:55:19 +02:00
DanB
cb442c3a9b LoadLcrRules inside LcgLoadAll, fix nil pointer error when stats not configured and requested by LCR strategy 2015-05-01 20:56:26 +02:00
DanB
aa5e56a32a ApierV1.GetLcr command implementation 2015-05-01 18:44:53 +02:00
DanB
4905bb15b8 CDRS should not store RawCdr in rated_cdrs table 2015-05-01 12:44:28 +02:00
DanB
014b17c93e Test *cdrlog params overload and ExtraFields in CDRs 2015-04-30 19:42:56 +02:00
DanB
1c11adfe37 Adding tests for loading ExtraParams/json in Actions.csv 2015-04-30 19:15:05 +02:00
DanB
e4a4732fee Dynamic template for *cgrlog, RSRFields instead of RSRField in extra_params 2015-04-30 13:28:59 +02:00
DanB
35de85f130 Adding actions_local_test.go file 2015-04-29 19:51:57 +02:00
DanB
5ce11a28a3 Action *cdrlog to StorDb, moved GetCallCost and LogCallCost in CdrStorage, local tests for *cdrlog 2015-04-29 19:50:03 +02:00
DanB
3ce339a592 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-04-29 12:28:59 +02:00
DanB
fa387f3515 Sample LCR rules in tutorial TP 2015-04-29 12:28:54 +02:00
Radu Ioan Fericean
ff8e8f98d1 first cdrlog tests 2015-04-28 20:47:01 +03:00
Radu Ioan Fericean
2e90814e30 started cdrlog 2015-04-28 20:47:01 +03:00
DanB
be1d789132 CDRS ignore CDR with ReqType=*none 2015-04-28 18:39:33 +02:00
DanB
720a26f6ca cgr_reqtype=*none to disable request processing in session manager 2015-04-28 18:36:09 +02:00
DanB
716249e4c9 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-04-28 18:21:07 +02:00
DanB
bc73d7b5f1 Correction getCost in tutorial - getCost does not consider balances, adding TCC and TCD in CDRST1 instance 2015-04-28 18:20:56 +02:00
Dan Christian Bogos
272fb8e9ac Merge pull request #64 from eloycoto/master
Fix *max_tcc and *min_tcc actions triggers
2015-04-28 18:02:35 +02:00
Eloy Coto
a9e9396b3c Fix *max_tcc and *min_tcc actions triggers 2015-04-28 15:54:17 +00:00
DanB
42cd26972f Tutorial cost calculation fix 2015-04-28 17:16:31 +02:00
DanB
8af467ed35 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-04-28 14:26:33 +02:00
DanB
0ad3433d41 Tutorial cost calculations fix 2015-04-28 14:26:21 +02:00
DanB
53c1439aca Fix CDRC sharing dataUsageMultiplyFactor and cdrSource 2015-04-28 13:15:40 +02:00
DanB
a85ed7040c CDR Replication better testing 2015-04-28 11:45:27 +02:00
Radu Ioan Fericean
da6ba8d75d updated lcr docs 2015-04-27 21:18:04 +03:00
Radu Ioan Fericean
2a327a55b5 more code docs 2015-04-27 21:18:04 +03:00
DanB
004defa882 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-04-27 19:46:33 +02:00
DanB
ddc61ac597 Allow CDRs with 0 duration to be sent to rater so we can get cost for them, thanks @eloycoto, issue #16 2015-04-27 19:46:21 +02:00
Radu Ioan Fericean
05c4974275 fix for #61
thank you eloycoto
2015-04-27 19:54:57 +03:00
DanB
aee3486a9c Initial setLcr implementation in SM-FreeSWITCH 2015-04-27 18:10:40 +02:00
DanB
81c3e13eaf Adding apier.v1 and v2 in test script 2015-04-26 13:02:38 +02:00
DanB
beb22e35f5 engine.CREDIT -> utils.MONETARY in apier tests 2015-04-26 12:09:41 +02:00
DanB
c1073d66c3 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-04-26 12:00:34 +02:00
DanB
9bf543af67 Adding tests for CDRC config load out of multiple config files 2015-04-26 11:58:18 +02:00
Radu Ioan Fericean
da0b9e641f fix build, again B| 2015-04-24 20:58:51 +03:00
Radu Ioan Fericean
812fe32017 fixed build 2015-04-24 20:54:56 +03:00
Radu Ioan Fericean
599b67e743 refactored balance types 2015-04-24 20:19:37 +03:00
Radu Ioan Fericean
4aea3b4167 debug values foe eloy 2015-04-24 18:18:29 +03:00
Radu Ioan Fericean
a20cd5d497 separate missing quos metrics from never calculated 2015-04-24 17:46:19 +03:00
Radu Ioan Fericean
a043548dab imroved lcr tests 2015-04-24 16:12:31 +03:00
DanB
aaaa73b48b Fix cdrstats test 2015-04-24 12:59:26 +02:00
DanB
cff7d47145 Correcting LCR tests 2015-04-24 12:51:05 +02:00
Radu Ioan Fericean
154552d0f6 make sure the ticker is stopped 2015-04-23 18:48:38 +03:00
Radu Ioan Fericean
b1ef36240f added TCD to LCR QOS 2015-04-23 14:37:03 +03:00
Radu Ioan Fericean
193fb458f6 added total call duration stats metric 2015-04-23 14:15:13 +03:00
Radu Ioan Fericean
1961965718 return -1 for stats metrics with no data 2015-04-23 14:07:50 +03:00
DanB
3c0a9005fd Merge branch 'master' of https://github.com/cgrates/cgrates 2015-04-23 11:41:49 +02:00
DanB
1324f57f56 Use global stats var when firing up CDR Server so we avoid uninitialized pointer 2015-04-23 11:36:28 +02:00
Radu Ioan Fericean
ee413d19c6 added ACC and TCC to LCR
what a nice commit message!
2015-04-22 23:16:29 +03:00
Radu Ioan Fericean
5c193bda8e added total call cost metric 2015-04-22 22:31:50 +03:00
Radu Ioan Fericean
188ff2010c fix console crash when it cannot connect 2015-04-22 19:16:41 +03:00
DanB
76c127ef59 Another small fix for #60 2015-04-22 17:55:37 +02:00
DanB
e340bfd307 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-04-22 17:44:29 +02:00
DanB
5154e3aa6f Fix issue #60, thanks @eloycoto 2015-04-22 17:39:38 +02:00
Radu Ioan Fericean
2ef4226145 only remove one account from actionplan (per API call) 2015-04-21 14:25:09 +03:00
DanB
7bab891161 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-04-21 11:10:38 +02:00
DanB
b346b28b3a Complete kamailio tutorial test calls 2015-04-21 11:10:33 +02:00
Radu Ioan Fericean
55193c47d1 lcr qos fixes 2015-04-21 00:22:27 +03:00
DanB
297dcbad58 Adding LCR Qos strategy tests 2015-04-20 10:03:26 +02:00
DanB
55f184111a Fix tutorial cdrstats.csv file 2015-04-17 21:40:15 +02:00
DanB
7623cec32e Adding LCR OoS Threshold strategy tests 2015-04-17 20:00:43 +02:00
DanB
6c291eb116 Renaming QOS_THRESHOLD 2015-04-17 18:03:49 +02:00
DanB
87c6217b40 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-04-17 12:09:09 +02:00
DanB
a62098cf64 Adding supplier in CdrStats filters 2015-04-17 11:31:11 +02:00
Radu Ioan Fericean
b5a273851b lcr fixes 2015-04-16 20:10:23 +03:00
Radu Ioan Fericean
99e1a0e2d6 abandoned redis mass insert 2015-04-16 18:25:44 +03:00
DanB
a1d7149263 Test LCR for bundles 2015-04-16 16:57:44 +02:00
DanB
57e1f0b571 Remove unused log package 2015-04-16 09:42:13 +02:00
DanB
b0e3e292b4 Disable debug log 2015-04-16 08:49:28 +02:00
DanB
27bb4b831a Parse LCR strategy parameters only when QOS is involved, compute QOS only when necessray, LCR test fix 2015-04-16 08:48:13 +02:00
DanB
6e28d2426d Merge branch 'master' of https://github.com/cgrates/cgrates 2015-04-15 20:33:15 +02:00
DanB
b47b4114ae Add singlecpu and cpuprofile flag options to engine 2015-04-15 20:33:06 +02:00
DanB
ff74c7c1fa Automatic console reconnects 2015-04-15 20:32:16 +02:00
Radu Ioan Fericean
1f4a105300 removed extra log 2015-04-15 20:10:26 +03:00
Radu Ioan Fericean
b5ebf2c4ef fixes for LCR test and code 2015-04-15 20:02:54 +03:00
Radu Ioan Fericean
8941a74cd9 started redis mass insert 2015-04-15 18:36:47 +03:00
DanB
3856f6ec81 Corrected LCR tests 2015-04-15 16:10:37 +02:00
DanB
ed69702cca Merge branch 'master' of https://github.com/cgrates/cgrates 2015-04-14 19:23:17 +02:00
DanB
0d147f42b4 APIer.Load* - reload scheduler only when APs are present, small responder fix for LCR, more LCR tests 2015-04-14 19:22:54 +02:00
DanB
f306d4b76c Console commands, load_tp_from_folder and load_tp_from_stordb 2015-04-14 16:43:17 +02:00
Radu Ioan Fericean
869efc4407 if lc RPCategory = *default use original category 2015-04-10 13:23:47 +03:00
DanB
919e00419a Fix Responder.TestGetLCRStatic 2015-04-10 10:07:39 +02:00
Radu Ioan Fericean
ed72e1a296 full supplier key 2015-04-10 10:24:33 +03:00
Radu Ioan Fericean
e6d92e7d70 use old GetCost if no lcr accounts defined 2015-04-10 10:05:30 +03:00
Radu Ioan Fericean
45f8243541 unig time.Duration in LcrSupplierCost 2015-04-10 09:49:49 +03:00
DanB
c281916089 Adding responder GetLCR static sample 2015-04-10 08:39:09 +02:00
DanB
229af3198a Merge branch 'master' of https://github.com/cgrates/cgrates 2015-04-09 09:04:26 +02:00
DanB
2c8fc24c00 DestinationRates.csv with empty maxCost parsing 2015-04-09 07:58:19 +02:00
Radu Ioan Fericean
393d01acba use weight in destination matching 2015-04-08 10:38:50 +03:00
Radu Ioan Fericean
4bda71a660 only one lcr entry per cd 2015-04-08 09:59:39 +03:00
Radu Ioan Fericean
34ca406e88 first draft for lcr docs 2015-04-07 21:27:46 +03:00
Radu Ioan Fericean
27f7e6d9ca fixed lcr csv order and started lcr docs 2015-04-07 13:59:21 +03:00
Radu Ioan Fericean
25a1858eac fixes and tests for lcr 2015-04-06 18:30:40 +03:00
Radu Ioan Fericean
4cc9789871 various lcr bits test 2015-04-06 14:59:44 +03:00
DanB
041646f38a Adding console PostprocessRpcParams method 2015-04-06 12:07:34 +02:00
DanB
60975a7e40 ApierV1.ImportTariffPlanFromFolder and ApierV1.LoadTariffPlanFromStorDb methods, various loader fixes and improvements 2015-04-05 17:59:43 +02:00
DanB
e4dc69755a Parsing of the Supplier from switch events 2015-04-04 18:55:30 +02:00
DanB
1d22a93cdc Allow re-rating of the derived cdrs 2015-04-04 18:13:55 +02:00
DanB
5c13fd36f7 Supplier in forkCdr function 2015-04-04 18:09:43 +02:00
DanB
8fa57a76cc Merge branch 'master' of https://github.com/cgrates/cgrates 2015-04-04 17:47:52 +02:00
DanB
f5803b8e9c Adding supplier in derived charging 2015-04-04 17:47:42 +02:00
DanB
1ec59ad794 Adding supplier field in StoredCdr and related, rename IgnoreDerived-> FilterOnDerived in getCdrs function, fixed postgres load from tp_rates 2015-04-04 16:35:59 +02:00
Radu Ioan Fericean
eb60e6caf5 fix for the lock issue 2015-04-03 22:33:16 +03:00
Radu Ioan Fericean
8037f99cb8 further account guard simplification 2015-04-03 21:49:52 +03:00
Radu Ioan Fericean
acf2b44fe3 simler lock for account guard 2015-04-03 21:21:14 +03:00
Radu Ioan Fericean
53a4895683 fix build and test on lcr qos sorter 2015-04-03 19:40:18 +03:00
Radu Ioan Fericean
4926e33528 simplified account locking and more lcr qos 2015-04-03 19:21:26 +03:00
DanB
a8038687fd Fix merge conflict 2015-04-03 18:10:13 +02:00
DanB
133e99ee23 Add utils.ReflectFuncLocation, remove debug for account locks 2015-04-03 17:59:40 +02:00
Radu Ioan Fericean
43c17326dc qos with thresholds for lcr 2015-04-03 14:40:44 +03:00
Radu Ioan Fericean
c100e171d4 added CdrStatQueueIds to rating profiles 2015-04-02 21:10:21 +03:00
Radu Ioan Fericean
60b3bb0f95 connect to cdrstats from rater 2015-04-02 18:25:47 +03:00
Radu Ioan Fericean
f050fe49df simplified ConcatenatedKey 2015-04-02 18:00:36 +03:00
Radu Ioan Fericean
c020ab02ea shaping lcr qos 2015-04-02 18:00:36 +03:00
Radu Ioan Fericean
5f7876937c small apier improvement 2015-04-02 18:00:36 +03:00
DanB
57827b3a3d Adding CdrStats config in rater 2015-04-02 16:52:31 +02:00
DanB
c908aa95cf Merge branch 'master' of https://github.com/cgrates/cgrates 2015-04-02 09:28:16 +02:00
DanB
4193e6981e Small fix fs_evapi test calls 2015-04-02 09:27:51 +02:00
DanB
32c5f924d5 More tests on fs_evapi and kamailio tutorials 2015-04-01 21:42:18 +02:00
Radu Ioan Fericean
837f9c98cb more work on lcr 2015-04-01 21:32:14 +03:00
DanB
d3619368fe Fix multiple connections handling in session manager 2015-03-31 21:39:05 +02:00
DanB
6efeacf817 Small fix postgres indexes 2015-03-31 19:29:17 +02:00
DanB
a29d17322d Merge branch 'master' of https://github.com/cgrates/cgrates 2015-03-31 19:23:29 +02:00
DanB
79d78867d3 Adding kamevapi automated calls and tests 2015-03-31 19:23:19 +02:00
DanB
fc2476633b Kamailio max session time fix from nanoseconds to seconds 2015-03-31 18:42:51 +02:00
Radu Ioan Fericean
66abb755cb new lcr structure 2015-03-31 18:25:06 +03:00
DanB
60754e86c0 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-03-27 18:32:36 +01:00
DanB
bb618c268f Adapting fs_evsock tutorial cgrates.json config with new CDRS configuration 2015-03-27 18:32:00 +01:00
DanB
7677a5ff89 Merged CDRS with Mediator for more code robusness and improve replication mechanism; adding CdrFilters inside CdrReplication 2015-03-27 18:29:28 +01:00
DanB
531b58e2ef SMFS - Adding sip_req_user as destination due to special cases like transfer, couple of tests more for session manager connections 2015-03-25 19:21:41 +01:00
Dan Christian Bogos
7480019833 Merge pull request #57 from eloycoto/master
Debian Packages: Cleanup on finish
2015-03-24 16:26:15 +01:00
Eloy Coto
b559a14882 Debian Packages: Cleanup on finish 2015-03-24 15:18:58 +00:00
DanB
28a76e616f Merge branch 'master' of https://github.com/cgrates/cgrates 2015-03-24 14:35:00 +01:00
DanB
3be8597f87 CdrsV1.ProcessExternalCdr API, CgrExtCdr renamed to ExternalCdr for better readability 2015-03-24 14:34:47 +01:00
Dan Christian Bogos
5ff9a18af3 Merge pull request #56 from eloycoto/master
Debian packages
2015-03-24 10:08:06 +01:00
Eloy Coto
9a25fca06d Debian packages: Initial work to compile current project 2015-03-23 20:36:58 +00:00
DanB
8f071af11e Correction in call tests 2015-03-23 18:26:27 +01:00
DanB
f2cffe071b Merge branch 'master' of https://github.com/cgrates/cgrates 2015-03-23 18:10:34 +01:00
Radu Ioan Fericean
97ef816839 fix for tutorial rates data 2015-03-23 19:10:07 +02:00
DanB
6c765fbe7f Merge branch 'master' of https://github.com/cgrates/cgrates 2015-03-23 17:40:35 +01:00
DanB
5645579fe7 Adding CostDetails to both StoredCdr and CgrExtCdr objects 2015-03-23 17:40:21 +01:00
Dan Christian Bogos
f7adfd9ce5 Merge pull request #55 from eloycoto/master
Fix changelog issues on debian jessie
2015-03-23 16:44:20 +01:00
Radu Ioan Fericean
93b4872db9 set max cost so far in session manager 2015-03-23 16:58:12 +02:00
Radu Ioan Fericean
938f2fada0 test for max cost strategy 2015-03-23 16:52:24 +02:00
Eloy Coto
67f6d1240f Fix changelog issues on debian jessie 2015-03-23 14:32:46 +00:00
Radu Ioan Fericean
643636872e moved max cost and strategy at destination rate level 2015-03-23 14:41:25 +02:00
Radu Ioan Fericean
9b68022110 update mgo import path 2015-03-23 11:45:34 +02:00
Radu Ioan Fericean
581648a04f max cost and strategy at rate level 2015-03-23 11:39:49 +02:00
DanB
44fa456eb5 Moving Cdr* from utils to engine package so we can attach CostDetails to StoredCdr 2015-03-22 18:04:38 +01:00
DanB
54e95dc929 CDRS CDR path change (from /cgr to /cdr_post), replication mechanism implementation for both raw and rated CDR 2015-03-22 15:03:42 +01:00
DanB
966f2f3a51 Adding libconfig source file 2015-03-19 11:21:15 +01:00
DanB
c39eb26bc7 Small fix config.CDRSCdrReplication 2015-03-19 11:20:09 +01:00
DanB
c26bfcdb1f Adding CdrReplication config for raw CDRS 2015-03-19 10:51:42 +01:00
DanB
98551316d6 Json config part for mediation CDR replication 2015-03-17 10:30:25 +01:00
DanB
dd6977e43d Change ReqType to use META(*) as prefix in front of the type for consistency with the rest of the system (eg: prepaid -> *prepaid 2015-03-15 19:16:50 +01:00
DanB
77d9d2ec30 Format time into RFC3339 for more compact representation, GetCdrs returning now CgrExtCdr with SetupTime, AnswerTime and Usage set as string for more interoperability with other languages 2015-03-15 14:18:30 +01:00
DanB
ab946cd637 Automated call testing for FS prepaid calling 2015-03-14 21:01:52 +01:00
DanB
e83eed741b Updated packaging scripts to reflect chages added for test calls framework and removing of oudated fs tutorials, thanks @rinor 2015-03-14 16:35:30 +01:00
DanB
50252c3efc ApierV1.LoadTariffPlanFromFolder fix for shared groups load 2015-03-14 10:34:48 +01:00
DanB
3d4ffd9dd9 Adding tutorial_fs_calls_test file 2015-03-13 19:22:25 +01:00
DanB
c47a9b54dc Merge branch 'master' of https://github.com/cgrates/cgrates 2015-03-13 19:16:15 +01:00
DanB
de96c183cf Framework for automated call testing 2015-03-13 19:16:11 +01:00
Dan Christian Bogos
e7c1225ab2 Merge pull request #52 from eloycoto/master
Add doc for derived charges
2015-03-12 13:17:14 +01:00
Eloy Coto
2211ed183d Add doc for derived charges 2015-03-12 12:08:51 +00:00
Radu Ioan Fericean
79750c6374 fix shared group member ids issue 2015-03-12 13:35:06 +02:00
DanB
ef0990b9dd Merge branch 'master' of https://github.com/cgrates/cgrates 2015-03-11 19:53:07 +01:00
DanB
f50b61d2c0 Small fix APIer 2015-03-11 19:53:02 +01:00
Radu Ioan Fericean
fbed64cd09 added test for max session duration from shared group 2015-03-11 20:41:54 +02:00
Radu Ioan Fericean
6125c44310 added sharedgroup console command 2015-03-11 20:38:37 +02:00
Radu Ioan Fericean
bf0ac94a8d updated copyright info 2015-03-11 19:49:52 +02:00
Radu Ioan Fericean
5679258f0e fix for not enough credit on max debit 2015-03-10 13:38:35 +02:00
DanB
c20fb69986 Fix SetActionPlan broken reference error 2015-03-10 12:36:52 +01:00
DanB
3fc4d60d3f Fix responder test 2015-03-10 12:10:16 +01:00
DanB
f3354080fc Responder.GetDerivedMaxSessionTime and GetSessionRuns now taking concrete types as parameters instead of interfaces for easy RPC integration, added fs_evsock tutorial files, removed fs_csv and fs_json to keep tutorial simple to maintain 2015-03-10 12:04:40 +01:00
DanB
c5fcc7cd31 Fix created_at and updated_at inside cost details, config defaults has now reconnect as 5 instead of invalid -1 2015-03-09 17:00:14 +01:00
DanB
df767aa142 Avoid concurrency in case of hangup coming in before answer for prepaid calls 2015-03-09 13:44:53 +01:00
DanB
d2cb001051 Fix session manager not properly reading events in multi connection mode 2015-03-09 13:34:17 +01:00
DanB
d48f617a02 Enforce travis build 2015-03-07 09:53:34 +01:00
DanB
96d4b6c28b Multiple SessionManagers started out out of the same engine, configuration refactoring completed for SessionManagers 2015-03-06 20:55:31 +01:00
DanB
a0d495647d Fixup SM-OpenSIPS json confiCCCg 2015-03-06 19:41:40 +01:00
DanB
6e84214d19 SM-Kamailio capable of multiple connections towards Kamailio servers 2015-03-06 19:36:00 +01:00
DanB
004687f541 SM-OpenSIPS to use new configuration format in .json 2015-03-06 19:10:22 +01:00
DanB
cb2ab3224b Refactored FreeSWITCH SessionManager to make use of multiple connections, give up sharing of configuration at package level, make better use of interfaces to communicate with Sessions 2015-03-06 17:30:12 +01:00
DanB
9301918159 Cost mutiply fix 2015-03-05 12:45:22 +01:00
DanB
6d4844afcf Fix ApierV1.LoadTariffPlanFromFolder on non-existent folder (thanks @catokx), increased category field to 32 char in SQL tables 2015-03-05 12:15:22 +01:00
DanB
db4a146924 Part two - fix session initialization leading to nil pointer error in session manager, refactored tutorial init scripts, use /tmp as run_dir 2015-03-04 18:29:47 +01:00
DanB
13de051fd1 Fix session initialization leading to nil pointer error in session manager, refactored tutorial init scripts, use /tmp as run_dir 2015-03-04 18:28:22 +01:00
DanB
893bb921e2 Allow nonexistent default configuration folder 2015-03-04 17:36:21 +01:00
DanB
4963c70b48 Fix paginator conversion between CDR filters, paginator tests for both mysql and postgres 2015-03-02 19:00:31 +01:00
DanB
d3b3794770 Gorm fixes, send pointer to value instead of value itself to Save method, Count method natively handled in SQL now 2015-03-02 18:33:40 +01:00
DanB
88daa45e3b Add websockets in external libs script, small local tests modification 2015-03-02 13:33:46 +01:00
DanB
720a333ed7 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-03-02 13:20:04 +01:00
DanB
f3e97e3392 Paginator parameters rename 2015-03-02 13:19:42 +01:00
Radu Ioan Fericean
ee4a358a78 use cgrates/gorm for stability 2015-03-02 14:18:36 +02:00
Radu Ioan Fericean
b46d15737e enable json rpc over websocket 2015-03-02 11:40:52 +02:00
DanB
a4bbad4001 TPActionTriggers-MinSleep from int to string 2015-02-24 09:50:14 +01:00
DanB
9bbc672ba1 ApierV1.RemAccountActionTriggers using ActionTriggersId 2015-02-20 16:47:54 +01:00
DanB
461b715c3b Adding indexes to postgres tp tables, ApierV1.AddBalance using BalanceId, ApierV1.AddTriggeredAction using ActionTriggersId 2015-02-20 16:44:08 +01:00
DanB
01f1b9aa64 ApierV1.GetMaxSessionTime, make direction, tenant, account and subject optional in ApierV1.SetDerivedChargers 2015-02-18 19:35:12 +01:00
DanB
8adc935817 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-02-16 17:06:42 +01:00
DanB
df27cc4bb0 Exporter fix for cost_details not being parsed by RSRField 2015-02-16 17:05:01 +01:00
Radu Ioan Fericean
11c8349329 cgradmin conf sample 2015-02-11 20:59:07 +02:00
DanB
1f459f759b Remove CONCATENATED_FIELD type since it is implemented through the use of combined RSRFields 2015-02-11 19:34:14 +01:00
DanB
654e0cfe12 CdrExporter using Value instead CdrFieldId for better flexibility 2015-02-11 19:31:22 +01:00
DanB
1fe2c60365 ApierV1.GetAccounts 2015-02-11 09:10:56 +01:00
DanB
bb01740321 RSRField filter checking empty values 2015-02-10 16:39:10 +01:00
DanB
a4aef0bb2d Merge branch 'master' of https://github.com/cgrates/cgrates 2015-02-10 12:35:40 +01:00
DanB
c189818356 CDRC fixup for nil import filters, adding old SM configuration options back until we are ready with new config structure 2015-02-10 12:35:32 +01:00
Radu Ioan Fericean
65a5eaf78b all tests passing 2015-02-09 20:48:22 +02:00
Radu Ioan Fericean
41cb3ab7d1 work in progress 2015-02-09 20:48:22 +02:00
DanB
1abc16882c Updating default .json configuration file 2015-02-09 14:02:04 +01:00
DanB
3f5540ce4c Tests fixes for ActionTriggers 2015-02-09 12:50:05 +01:00
Radu Ioan Fericean
dc5111eedb preserve tag 2015-02-09 13:26:58 +02:00
Radu Ioan Fericean
dbcc21c4d5 fix for tp actiontrigger id 2015-02-09 13:17:25 +02:00
DanB
56b37feafe Merge branch 'master' of https://github.com/cgrates/cgrates 2015-02-08 20:54:06 +01:00
DanB
6ed45a52fe RSRField with filterValue support, CDRC implementing instances based on processed folder, import filters 2015-02-08 20:53:56 +01:00
Radu Ioan Fericean
f0fc38e62a more tests and fixes 2015-02-06 19:02:52 +02:00
Radu Ioan Fericean
d10366457a added extra uniqueid for action triggers 2015-02-06 15:34:24 +02:00
DanB
c3b885d79d Merge branch 'master' of https://github.com/cgrates/cgrates 2015-02-06 13:25:03 +01:00
DanB
ff68023ca5 ApierV2.GetAccounts 2015-02-06 13:24:51 +01:00
Radu Ioan Fericean
0d9f04e030 fix compilation error 2015-02-05 23:25:17 +02:00
Radu Ioan Fericean
162fd28723 fix osipevent recursive String 2015-02-05 23:20:50 +02:00
Radu Ioan Fericean
1c16419bb3 more go vet 2015-02-05 23:20:50 +02:00
Radu Ioan Fericean
7eaf38a683 utils vet 2015-02-05 23:20:50 +02:00
Radu Ioan Fericean
48bf322b16 apier vet 2015-02-05 23:20:50 +02:00
Radu Ioan Fericean
4ff96becee human readable action and action trigger id 2015-02-05 23:20:50 +02:00
Radu Ioan Fericean
39a85a76de cleanned all vet's warnings 2015-02-05 23:20:50 +02:00
DanB
8576d8f6fc CdrExporter SmsUsageMultiplyFactor implementation 2015-02-05 18:20:39 +01:00
DanB
211f980329 Default RunId changed to *default for consistency, StoredCdr.Rated property so we can import already calculated CDRs, fixes for SkipErrors and SkipRated API filters for CDRs, created_time fix on MySQL, fix injected Cost when importing from external file and .Rated is false 2015-02-05 17:01:45 +01:00
DanB
98935b99b7 Responder.TestGetDerivedMaxSessionTime fix 2015-02-04 15:06:54 +01:00
DanB
8e4fe785fc Merge branch 'master' of https://github.com/cgrates/cgrates 2015-02-04 14:18:37 +01:00
DanB
afb8aae211 GetStoredCdrs.Count temporary fix until gorm analyses our reported issue 2015-02-04 13:56:33 +01:00
Radu Ioan Fericean
73096da0b6 fix data matched prefix missing 2015-02-03 20:57:40 +02:00
DanB
490bd9d573 ApierV2.GetAccountIds, remove utils.EMPTY symbol, fix /etc/cgrates folder in packaging, thanks esamuels 2015-02-03 19:56:27 +01:00
DanB
bc169e24d0 Test TestGetDerivedMaxSessionTime modified for rif's review 2015-02-02 21:31:50 +01:00
DanB
08dee64412 Remove realcalls_test since it becomes hard to maintain it 2015-02-02 17:37:43 +01:00
DanB
33fc03f8fb Merge branch 'master' of https://github.com/cgrates/cgrates 2015-02-02 17:26:50 +01:00
DanB
b8fe5bc0f8 Test fixes for cdrc exit mechanism implementation 2015-02-02 17:26:39 +01:00
DanB
fc69b29980 Adding Sm*Config instances, Cdrc stop mechanism to prepare for reload configuration 2015-02-02 17:22:26 +01:00
Radu Ioan Fericean
347e78d5b5 new core rating rules 2015-01-27 18:57:08 +02:00
DanB
9f785f58b9 Another small fix packaging 2015-01-22 10:56:32 +01:00
DanB
ea76366ead Fix debian package config path, thanks esamuels 2015-01-22 10:44:51 +01:00
DanB
95b1420bf8 Index answer_time and deleted_at columns for faster CDR queries/exports 2015-01-21 11:21:27 +01:00
Radu Ioan Fericean
e440bdf37e handle http for rpc rquests at /jsonrpc 2015-01-20 18:16:02 +02:00
Radu Ioan Fericean
a75035af3c balance activation times (work in proggress) 2015-01-20 18:16:02 +02:00
DanB
18b90fb96b Clone default configured cdre and cdrc instances to avoid pointer inheritage 2015-01-20 13:13:33 +01:00
DanB
1a02245f3f Multiple configuration files local tests 2015-01-18 19:53:49 +01:00
DanB
97e2bc9a22 Local tests fixups 2015-01-18 18:09:07 +01:00
DanB
eed6e4dfa9 Exempt local config file from travis tests 2015-01-18 13:00:26 +01:00
DanB
1cb581ed23 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-01-18 12:54:33 +01:00
DanB
6126b69c4e NEW configuration format - cgrates.json 2015-01-18 12:54:07 +01:00
Radu Ioan Fericean
7528b6652c Only latest go stable version 2015-01-13 14:23:21 +02:00
Dan Christian Bogos
afc9a9a97a Merge pull request #37 from eloycoto/eloydoc
Update ActionsTriggers in tarifplans
2015-01-08 13:59:23 +02:00
Eloy Coto
9f66bedde9 Update acctionTriggers options in cgrates
Update a few phrase
Add eloycoto in Contributors
2015-01-08 11:48:20 +00:00
DanB
bf27fa3512 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-01-06 17:36:54 +01:00
DanB
3303608813 Basic json config structures and defaults with tests 2015-01-06 17:36:32 +01:00
Radu Ioan Fericean
ef101ce3b0 nil balance map fix
fix for adding default balance on uninitialized account
2015-01-06 14:21:40 +02:00
Radu Ioan Fericean
6881d84054 protection against malformed destination keys 2015-01-06 10:32:29 +02:00
DanB
bb3bed0f5b Merge branch 'master' of https://github.com/cgrates/cgrates 2015-01-05 20:44:54 +01:00
DanB
11e49d90e0 Adding JsonConfigReader to external libs 2015-01-05 20:44:40 +01:00
Radu Ioan Fericean
e2bb19a9dd added go 1.4 to travis build list 2015-01-05 21:38:07 +02:00
DanB
b51b42025b Limiting destination reloads for ApierV1.LoadDestination and adding PreventCacheReload parameter to API 2015-01-05 20:34:46 +01:00
DanB
3e37cb8368 Initial config.json skel with tests 2015-01-05 20:11:39 +01:00
Radu Ioan Fericean
6fd5b8b356 added kamailio lib 2015-01-05 10:49:49 +02:00
DanB
843ad95763 Merge branch 'master' of https://github.com/cgrates/cgrates 2015-01-04 19:34:23 +01:00
DanB
2b72490bbd Sample cgrates.json configuration file 2015-01-04 19:24:34 +01:00
DanB
10bbf73596 DisconnectSession with event instead of uuid to be more flexible in components where uuid is not enough to kill dialog (eg kamailio) 2015-01-01 16:59:35 +01:00
DanB
b9002e674d Using includes in kamailio.cfg for better config script readability 2015-01-01 15:58:01 +01:00
DanB
ea3a9e6dee Responder.GetSessionRuns, SM-Kamailio with prepaid support 2015-01-01 15:57:21 +01:00
DanB
39acb88824 Small correction kamailio.cfg skel 2014-12-29 15:03:50 +01:00
DanB
35f5a69311 Kamailio postpaid, rated, pseudoprepaid support with derived charging 2014-12-29 09:16:54 +01:00
DanB
180d54391d Fix regex rules for BalanceTimingTags csv parsing 2014-12-26 13:01:49 +01:00
DanB
29dda3ecfe Merge branch 'master' of https://github.com/cgrates/cgrates 2014-12-26 11:53:18 +01:00
DanB
669283d355 Adding kamevapi in the update_external_libs script 2014-12-26 11:48:03 +01:00
DanB
52bd163746 Loader changes, adding BalanceTag in Actions.csv and ActionTriggers.csv 2014-12-26 11:46:54 +01:00
DanB
6808f41414 Following changes in gorm, fix tests, removed count CDRs 2014-12-26 11:43:08 +01:00
Radu Ioan Fericean
0bb8691101 balance activation times 2014-12-22 23:02:14 +02:00
DanB
65e0597873 Moved event to utils package so we can use it in engine 2014-12-22 18:51:59 +01:00
DanB
ff60061a84 Event interface containing AsEvent instead of New, for better readability outside of sessionmanager package, storedCdr supporting event interface for testing purposes, Responder.GetDerivedMaxSessionTime tests 2014-12-22 18:47:12 +01:00
DanB
3c2c21e990 Responder.GetDerivedMaxSessionTime with build in derived charging calculation 2014-12-22 08:29:05 +01:00
DanB
fb019955ca Adding Kamevent parsing 2014-12-21 11:42:41 +01:00
DanB
50ce6ed351 Initial kamevent skel 2014-12-19 20:18:11 +01:00
DanB
bcb7848c5b Skel of kamailio session manager 2014-12-19 18:55:01 +01:00
DanB
48890e7486 CDRC accepting float64 instead of int64 as DataChargeMultiply 2014-12-17 17:18:17 +01:00
DanB
9c7b8bd5b9 Outdated localtest fix balances 2014-12-07 13:30:18 +01:00
DanB
f583c24a6b Merge branch 'master' of https://github.com/cgrates/cgrates 2014-12-07 12:40:23 +01:00
Radu Ioan Fericean
26c6c22832 match empty filters 2014-12-07 13:39:52 +02:00
DanB
cd81e7865a Merge branch 'master' of https://github.com/cgrates/cgrates 2014-12-07 12:27:25 +01:00
DanB
18d173d0fb Small correction Apier test 2014-12-07 12:27:17 +01:00
Radu Ioan Fericean
3df2ee6d5f more fixes and tests on balance filter matching 2014-12-07 13:26:16 +02:00
DanB
0dab19aa12 Merging with master 2014-12-07 12:08:37 +01:00
Radu Ioan Fericean
660e57f085 changed column names in sql scripts as well 2014-12-07 12:56:21 +02:00
Radu Ioan Fericean
0b42f88374 better uuid4 function 2014-12-06 21:40:03 +02:00
DanB
0115b5ba6f Merge branch 'master' of https://github.com/cgrates/cgrates 2014-12-05 19:25:26 +01:00
DanB
799d924776 Increase min_sleep in TP tables from int to bigint since otherwise gets trucated 2014-12-05 19:24:56 +01:00
DanB
328bb416b2 Tests fixup using the new functions in libtests 2014-12-05 18:55:25 +01:00
DanB
91ff8f5147 Engine to initialize database only when needed, improved db.Flush(), more CdrsV2 local tests 2014-12-05 18:43:30 +01:00
Radu Ioan Fericean
9b40c20915 topup/debit reset only reseting matched balances 2014-12-05 13:26:38 +02:00
Radu Ioan Fericean
20edf5d070 xompilation fix 2014-12-04 20:42:07 +02:00
Radu Ioan Fericean
524e2cc94d added balance tag for action 2014-12-04 20:13:19 +02:00
Radu Ioan Fericean
2dd3f6527d added balance tag for action triggers
also chenged the names of direction and destination params (prefixxed
with balance)
2014-12-04 20:13:19 +02:00
DanB
dd5cf17d7b Initial tests for CdrsV2 for both mysql and postgres 2014-12-04 14:31:26 +01:00
DanB
4711fc7176 Adding initial folder for kamevapi tutorial 2014-12-03 09:59:57 +01:00
DanB
310d47515b Merge branch 'master' of https://github.com/cgrates/cgrates 2014-12-02 20:41:17 +01:00
DanB
9d81ee28c5 Initial skel kamailio config 2014-12-02 20:40:19 +01:00
Radu Ioan Fericean
33a2ff39d6 protect against rescheduling actions with bad type 2014-12-01 15:37:55 +02:00
DanB
6ae76238d5 Fixups postgres as stordb for offline TP data 2014-11-30 20:34:09 +01:00
DanB
54669c93a0 Init script with automatic stacktrace 2014-11-30 17:37:26 +01:00
DanB
a456706b99 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-11-28 11:48:18 +01:00
DanB
5f88e621f4 Fix CountCdrs and adding tests for it 2014-11-28 11:48:05 +01:00
Radu Ioan Fericean
1665113742 small optimizations for cache 2014-11-27 22:36:42 +02:00
DanB
42a71347e8 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-11-27 20:31:36 +01:00
DanB
24866c8460 ApierV2.CountCdrs method 2014-11-27 20:30:46 +01:00
DanB
bb3f677f50 Adding count parameter to GetStoredCdrs 2014-11-27 20:27:36 +01:00
Radu Ioan Fericean
5e7eec3685 updated copyright year 2014-11-27 21:27:00 +02:00
DanB
f377fd34be Merge branch 'master' of https://github.com/cgrates/cgrates 2014-11-27 19:35:39 +01:00
DanB
fdfb90a16d Adding ApierV2.GetCdrs method 2014-11-27 19:34:31 +01:00
Radu Ioan Fericean
29821162a2 eliminated engine test race 2014-11-27 19:52:32 +02:00
DanB
b53f18381e Merge branch 'master' of https://github.com/cgrates/cgrates 2014-11-27 18:01:53 +01:00
DanB
93a7e59744 GetStoredCdrs taking now a struct as filter to simplify code testing it 2014-11-27 18:01:41 +01:00
Radu Ioan Fericean
d0fb4aab90 added coredump file to debian init command 2014-11-27 15:37:31 +02:00
Radu Ioan Fericean
98d3bd6cde fixed status command in console 2014-11-27 14:52:06 +02:00
Radu Ioan Fericean
3e03b328b0 added destinations cache threshold 2014-11-27 14:44:20 +02:00
Radu Ioan Fericean
b7891b87ba type fixes 2014-11-27 12:10:33 +02:00
Radu Ioan Fericean
dac0248c15 load all destination in cache if more than 0.5 2014-11-27 12:06:47 +02:00
Radu Ioan Fericean
4735c7c0a4 updates simple cache store too 2014-11-26 18:30:40 +02:00
Radu Ioan Fericean
680985200f Using map[interface{}]struct{} instead of []interface{} in cache 2014-11-26 18:28:07 +02:00
Radu Ioan Fericean
728354331b sort only once for slice 2014-11-26 17:44:32 +02:00
Radu Ioan Fericean
72c3d1ac29 another optimization for stale dest ids 2014-11-26 16:39:33 +02:00
Radu Ioan Fericean
5f7fac2f64 restructuring and optimizations
- moved misc and pkg to data/scripts
- started recover procedure
- optimized clean stale ids
2014-11-26 15:07:14 +02:00
DanB
489c17c561 Refactored GetStoredCdrs method to increase the number of items being queried 2014-11-26 13:48:07 +01:00
DanB
63faee90b2 Fix doc formatting issue, thanks @dyer 2014-11-19 18:45:31 +01:00
DanB
030d24c417 Common GetStoredCdrs for MySQL and Postgres, partially using Gorm 2014-11-17 14:21:31 +01:00
DanB
ad4d86ab6f Small test fixup apier 2014-11-14 20:07:07 +01:00
DanB
f31ea16099 Enabling postgres config within storage_utils 2014-11-14 20:01:38 +01:00
DanB
edd8d5540f Initial Postgres support 2014-11-13 19:41:15 +01:00
DanB
d3bce53d40 SQL GetCallCostLog using gorm 2014-11-13 14:27:32 +01:00
DanB
cbd3e57397 Different GetCallCost for MySQL and Postgres 2014-11-13 12:01:42 +01:00
DanB
afb4a7e39f Update CreatedAt when working on TP inserts 2014-11-12 18:59:02 +01:00
DanB
da21ffd32c CRUD Timestamps with no defaults 2014-11-12 10:58:30 +01:00
DanB
dd132805ec Adding automatic timestamp columns to SQL 2014-11-12 10:19:29 +01:00
DanB
94317ec4f8 TP data management with PostgreSQL support 2014-11-11 11:04:39 +01:00
DanB
c773c82567 Diferential SQL testing for MySQL and PostgreSQL 2014-11-08 20:13:23 +01:00
DanB
1e9f1131ba MySQL and PostgreSQL implementing now their own Flush() 2014-11-08 18:25:55 +01:00
DanB
8517b9b8c8 PostgreSQL schema and user creation 2014-11-08 18:23:48 +01:00
DanB
a05f110383 Adding Cdr table models for gorm 2014-11-04 17:25:02 +01:00
DanB
5f05a2270f SQL table.*_id into table.*_tag to finish migration towards tags instead of ids 2014-11-03 16:51:16 +01:00
DanB
d7dd56035f id->tag, tbid->id for more SQL best practices 2014-11-03 16:18:09 +01:00
Radu Ioan Fericean
63e73df8d3 failed attempt to make the test pass 2014-10-30 17:39:17 +02:00
DanB
74b1574e64 Small doc fixup 2014-10-23 19:00:05 +02:00
DanB
61325a13aa Merge branch 'master' of https://github.com/cgrates/cgrates 2014-10-22 13:45:33 +02:00
DanB
cbc7a5095c Adding DataUsageMultiplyFactor within CDRC 2014-10-22 13:45:21 +02:00
DanB
573aeea9be Adding CdrcConfig.DataUsageMultiplyFactor config option 2014-10-22 11:47:01 +02:00
Radu Ioan Fericean
8df256fb8d converted string param to struct 2014-10-21 14:14:43 +03:00
DanB
63117baf6d TPExporter - Init the cache buffer before operating it 2014-10-21 10:33:24 +02:00
DanB
d6c97eec98 TPExporter considering fileFormat 2014-10-21 10:30:54 +02:00
DanB
771e0c49dc Fix TPExporter not properly closing the zipWriter 2014-10-20 20:06:43 +02:00
DanB
c9f2d5d5a2 TPExporter with compress function, ApierV2.ExportTPToZipString method 2014-10-20 19:21:52 +02:00
DanB
f57c97de8d Small test fixup 2014-10-20 12:59:37 +02:00
DanB
bdf354afeb ApierV2.ExportTPToFolder 2014-10-20 12:46:47 +02:00
DanB
4ef86a2dec Adding tpexport_dir config option 2014-10-20 11:25:28 +02:00
DanB
99e3a74604 Completing TPExporter methods 2014-10-20 11:12:35 +02:00
DanB
8a0011eda2 More methods on TPExporter 2014-10-19 20:40:55 +02:00
DanB
a1bce1f234 Adding missing tpexporter.go file 2014-10-17 19:51:26 +02:00
DanB
b7b22a39e0 Partial implementation of TPExporter, adding CgrRecordWriter interface 2014-10-17 19:49:04 +02:00
DanB
178e02efb9 Removing logging introduced for Mo's tests 2014-10-16 19:58:07 +02:00
DanB
d5f67b1490 Log duration returned with error 2014-10-16 19:34:39 +02:00
DanB
ddfbc88cdf Adding some importers 2014-10-16 19:19:24 +02:00
DanB
f9807bbcb0 More logging 2014-10-16 19:18:21 +02:00
DanB
da2b1275f7 Small fix debug 2014-10-16 18:56:42 +02:00
DanB
d379726cc5 Log in groups 2014-10-16 18:56:10 +02:00
DanB
53a1b07016 Adding some more log for Mo's case 2014-10-16 18:33:37 +02:00
DanB
879a86aa04 Adding log to session manager for Mo's troubleshooting 2014-10-16 14:17:29 +02:00
DanB
2a4c00fd77 Completing AsExportSlice methods for TP data 2014-10-15 12:28:06 +02:00
DanB
1c7b155c96 Some more methods for AsExportSlice in TP data 2014-10-14 20:13:27 +02:00
DanB
293a5e3821 Adding CONTRIBUTING skel 2014-10-14 12:12:51 +02:00
DanB
baddb02357 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-10-12 16:17:51 +02:00
DanB
80bd2c9275 Partial TPData as exportSlice mathods 2014-10-12 16:17:46 +02:00
DanB
f1927bcd39 Adding RemTP api file 2014-10-12 13:57:29 +02:00
Radu Ioan Fericean
f5cf570fd3 better transaction handling 2014-10-10 14:19:53 +03:00
DanB
0ea14cb4b8 storage_sql.RemTPData completed with gorm 2014-10-09 16:21:03 +02:00
DanB
c744cd5ef2 Small local test fixup following AttrGetTPRatingPlan modifications 2014-10-09 14:22:30 +02:00
DanB
cfa42ac692 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-10-09 14:12:07 +02:00
DanB
6c02605cfd ApierV2.RemTP 2014-10-09 14:11:58 +02:00
Radu Ioan Fericean
4033a7075e start using paginator embeded object 2014-10-08 21:33:50 +03:00
Radu Ioan Fericean
4092490a3d fix test 2014-10-07 11:45:41 +03:00
Radu Ioan Fericean
c0b4a13257 more logging 2014-10-07 11:30:36 +03:00
Radu Ioan Fericean
1b7f3861c1 log time on test server 2014-10-07 11:26:28 +03:00
Radu Ioan Fericean
4b3c405dda fix account setting checks 2014-10-07 11:01:49 +03:00
Radu Ioan Fericean
578453c766 nu more empty balance map checking 2014-10-07 10:16:26 +03:00
DanB
bafb0b0eb8 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-10-06 16:54:27 +02:00
DanB
ca493e109f Cdrc - http enhancements 2014-10-06 16:54:15 +02:00
Radu Ioan Fericean
ea57ce1384 more specifc error messages 2014-10-06 15:45:26 +03:00
DanB
44c6a3f9fb Cdrc Http fields should be lazy processed 2014-10-06 10:27:07 +02:00
DanB
b087baf068 CDRC cdr_type-> cdr_format, csv_separator->field_separator for better generics 2014-10-05 17:26:15 +02:00
DanB
47bc77d8e3 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-10-03 20:36:09 +02:00
DanB
da5652c7c0 ActionTiming should execute EnableAccount always when requested 2014-10-03 20:34:36 +02:00
Radu Ioan Fericean
732cd57538 filters for SearchTerm 2014-10-03 19:15:39 +03:00
DanB
e3277373fd JSON serializing cdr record on CDRC before sending it via http 2014-10-03 16:56:53 +02:00
DanB
3756919342 Adding maximumConnections and maximumIdleConnections on database side 2014-10-03 16:30:50 +02:00
DanB
d8868bf05b Test fixes in cdrc 2014-10-03 15:16:18 +02:00
DanB
1907d84d58 Consts fixup 2014-10-03 14:44:04 +02:00
Radu Ioan Fericean
8dbec8e60e fix failing test 2014-10-03 14:04:24 +03:00
DanB
0a9c6d3844 Small local test fix so Travis will behave 2014-10-02 19:34:29 +02:00
DanB
cd5a44f545 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-10-02 19:04:54 +02:00
DanB
789db4b8b4 Adding the new files for refactored work 2014-10-02 19:04:44 +02:00
DanB
e4c9cf561a Refactoring CDRE and CDRC configurations and functionality 2014-10-02 19:02:23 +02:00
Radu Ioan Fericean
88791968ef pagination for cdrs and more api for cache and accounts 2014-10-01 18:28:38 +03:00
Radu Ioan Fericean
b669f2e8ac using extrenal lib for next date 2014-09-30 19:05:41 +03:00
Radu Ioan Fericean
5e5aa78579 fix failing test 2014-09-30 12:02:03 +03:00
Radu Ioan Fericean
b4d743b73b saner hoepfully better GetNextTime
also improved status response
2014-09-30 00:21:51 +03:00
DanB
f609acd335 ExportCdrsToZipString keeping original file name and generating now metadata file 2014-09-23 14:07:53 +02:00
DanB
134f0ead17 FieldSeparator as string instead of rune inside ExportCdrs API 2014-09-23 12:21:02 +02:00
DanB
a67def57b9 Usage as imported/exported as floated seconds instead of nanoseconds 2014-09-23 11:54:43 +02:00
DanB
bfcb317501 ExportCdrsToZipString API when there are no records to be exported 2014-09-22 18:41:24 +02:00
DanB
7454bc14f4 CdrExporter API should ignore empty fields 2014-09-22 17:36:17 +02:00
DanB
e197cef030 Reordering ActionTriggers.csv for eficiency purposes 2014-09-22 13:28:14 +02:00
DanB
fd84f0e90e Modified Actions.csv fields order to make easier it's understanding 2014-09-21 20:25:36 +02:00
DanB
13eabc6fee Adding CDRStatsEnabled checking in case of internall used to avoid nil references 2014-09-21 14:42:55 +02:00
DanB
09c392dfa5 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-09-19 16:40:07 +02:00
Radu Ioan Fericean
f79a173aa6 refactor for v1 and v2 api
added Load api for multiple id resources
2014-09-18 19:58:50 +03:00
Radu Ioan Fericean
788d3c37a4 load cdr stats and shared groups 2014-09-16 17:00:02 +03:00
Radu Ioan Fericean
59bb827dd8 load derived chargers apier method 2014-09-15 19:57:12 +03:00
DanB
340300f2db Sleep after pkill in local tests so the sockets are freed 2014-09-10 18:25:21 +02:00
Radu Ioan Fericean
4d2ff245ad small load destination fix 2014-09-10 18:07:43 +03:00
DanB
57f8fbbcfb Merge branch 'master' of https://github.com/cgrates/cgrates 2014-09-10 16:10:55 +02:00
DanB
e01a2d166d Changing packaging version to rc6 2014-09-10 16:10:50 +02:00
Radu Ioan Fericean
6ecabf1d90 using _empty_ instead of *any for api 2014-09-10 15:22:04 +03:00
DanB
01d0b1b437 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-09-09 19:12:09 +02:00
DanB
f71aa8483c Load DerivedChargers 2014-09-09 19:11:25 +02:00
Radu Ioan Fericean
2f739a3897 added load destinations to apier and stordb 2014-09-09 14:30:09 +03:00
Radu Ioan Fericean
013ff6c6f2 fixes + easy switch between cache stores 2014-09-05 22:25:20 +03:00
Radu Ioan Fericean
46fc12e0cd fixes for loading apier methods 2014-09-04 21:33:13 +03:00
DanB
886ca68eb8 dataDb should not be read when cache is searched, tests updates 2014-09-04 18:36:46 +02:00
Radu Ioan Fericean
7b2ba2aeb9 turbo charged cache at 2:30am 2014-09-04 02:28:10 +03:00
Radu Ioan Fericean
9467f5741b count keys on CachePush too 2014-09-03 20:03:01 +03:00
Radu Ioan Fericean
24c79b6b22 working version for cache transactions 2014-09-03 19:17:55 +03:00
Radu Ioan Fericean
90f77cf158 one more test 2014-09-03 19:17:54 +03:00
Radu Ioan Fericean
fa7b97b5dc work on cache transactions 2014-09-03 19:17:54 +03:00
Radu Ioan Fericean
c5fea8ef71 simplified cache 2014-09-03 19:17:54 +03:00
DanB
8e0e888c29 TpImporter for CdrStats 2014-09-02 20:12:04 +02:00
DanB
067188ff43 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-09-02 18:51:45 +02:00
DanB
5a33e70793 TPImporter for SharedGroups and DerivedCharging 2014-09-02 18:37:11 +02:00
DanB
1e75bfed20 TP_ID_SEP -> CONCATENATED_KEY_SEP, apier_local_tests fixes for gorm 2014-09-02 16:58:16 +02:00
DanB
0450a08d3f Update SQL remove local tests to cope with gorm errors 2014-09-02 11:57:11 +02:00
DanB
62f53af8c0 Modified TPImporter to cope with new storage_sql methods 2014-09-02 11:00:36 +02:00
Radu Ioan Fericean
165fad5849 clean expired balances on debit 2014-09-01 15:26:04 +03:00
DanB
0fbd76abea Fix ActionTriggers to load Category from tariffplans folder 2014-09-01 12:28:50 +02:00
Radu Ioan Fericean
ccb7186a33 import to stor and started cdr export 2014-08-31 18:36:03 +03:00
Radu Ioan Fericean
153ca2c2fd add csv zip file import apier method 2014-08-30 17:06:13 +03:00
Radu Ioan Fericean
97d2277642 remove query logging 2014-08-25 19:41:28 +03:00
Radu Ioan Fericean
c232640363 add pagination and search for resource ids 2014-08-25 19:31:28 +03:00
Radu Ioan Fericean
f30b3dc063 add rating profile tp and apier methods 2014-08-25 16:27:09 +03:00
DanB
c1d5f00956 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-08-25 14:10:45 +02:00
DanB
ad69920c29 FSExtraFields fix, thanks DigiDaz 2014-08-25 14:10:40 +02:00
Radu Ioan Fericean
7dc72725fc add account actions tp and apier methods 2014-08-25 14:49:09 +03:00
Radu Ioan Fericean
d0597921df add Category to ActionTriggers 2014-08-24 18:28:05 +03:00
Radu Ioan Fericean
1ea0341db0 add Category to Actions 2014-08-24 18:08:10 +03:00
Radu Ioan Fericean
94884e24c9 small fixes for starting accounts and profiles 2014-08-24 16:34:31 +03:00
Radu Ioan Fericean
fd045e8b38 add derived chargers tp and apier methods 2014-08-24 13:56:13 +03:00
Radu Ioan Fericean
46c059bdb7 add cdr stats tp methods 2014-08-22 16:29:45 +03:00
Radu Ioan Fericean
5df1c52246 add tp shared groups methods and apier 2014-08-22 13:00:20 +03:00
Radu Ioan Fericean
a38339aef6 fixed tp action triggers methods 2014-08-21 21:24:39 +03:00
Radu Ioan Fericean
21664d8864 fixed tp action plans methods 2014-08-21 20:52:29 +03:00
Radu Ioan Fericean
dad3c605e7 fixed tp methods for action 2014-08-21 20:24:18 +03:00
Radu Ioan Fericean
16ad41a7b6 fix more tp methods 2014-08-21 19:52:47 +03:00
Radu Ioan Fericean
6a19284589 start using gorm as ORM 2014-08-21 17:25:22 +03:00
DanB
701c701a80 FreeSWITCH 1.4 introduces sleep if using default accounts, removed that 2014-08-18 19:09:15 +02:00
DanB
1d622cbd14 Fix package script to properly include freeswitch tutorial samples 2014-08-18 13:41:32 +02:00
DanB
3787ee64d5 Fix tut_fs_json/freeswitch config files 2014-08-18 12:16:15 +02:00
DanB
cf02179cde TutOpenSIPS documentation improvements 2014-08-18 11:39:36 +02:00
DanB
35e120e3f6 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-08-18 11:14:49 +02:00
DanB
5c36dbc62e Updating tutorials with OpenSIPS event 2014-08-18 11:14:08 +02:00
Radu Ioan Fericean
0f95e986a7 fixed typo 2014-08-17 19:19:28 +03:00
DanB
616a81279e Updating documentation with cdrstats, derived_charging 2014-08-17 14:23:17 +02:00
DanB
988f3c6bf2 Fix RPAlias loading 2014-08-16 20:16:17 +02:00
DanB
53f19dad4a Updating tutorials documentation, modified tariffplans/tutorial files 2014-08-16 13:21:34 +02:00
DanB
d1bd2700fd Adding cdrstats_* verification commands in the cgrates_usage tutorial, updating freeswitch vanilla samples with latest of 1.4 2014-08-15 19:06:37 +02:00
DanB
e45742761e More tutorial documentation refactoring 2014-08-15 15:31:05 +02:00
DanB
410ecfdd43 More tutorial documentation refactoring 2014-08-15 14:57:58 +02:00
DanB
36ae05cde2 FreeSWITCH Tutorial refactoring 2014-08-15 14:25:54 +02:00
DanB
b24c1982bf Adding StatsQueueTriggered to enhance the information displayed in triggered actions for StatsQueue 2014-08-15 13:20:44 +02:00
DanB
54e7c8a971 Adding cdrstats metrics to email and log actions 2014-08-13 18:05:05 +02:00
Radu Ioan Fericean
af4b38ffd5 removed goroutine action exec from scheduler 2014-08-13 17:00:16 +03:00
Radu Ioan Fericean
6c6b27a9d9 fix for local init it test files 2014-08-13 16:49:58 +03:00
Radu Ioan Fericean
75c8253756 made map storage behave more like redis one 2014-08-13 16:49:58 +03:00
DanB
f9204b54aa Merge branch 'master' of https://github.com/cgrates/cgrates 2014-08-13 14:38:13 +02:00
DanB
bba82e7c91 SM-FreeSWITCH: process all CDRs instead of just prepaid/pseudoprepaid, adding cdrstats to tariffplans/tutorial folder 2014-08-13 14:38:08 +02:00
Radu Ioan Fericean
b287c82ec3 removed drone.io badge 2014-08-13 14:47:37 +03:00
Radu Ioan Fericean
98996edc7e drone.io is out of service 2014-08-13 14:44:13 +03:00
Radu Ioan Fericean
6342c4a3e5 save *asap action timings with empty account id list 2014-08-13 14:38:52 +03:00
DanB
c64dae59d4 Adding GetId method to StatsQueue, mailAsync action improvements 2014-08-06 17:33:35 +02:00
DanB
29eff410e7 Putting back package files 2014-08-06 01:47:23 +02:00
DanB
1ff2af86d4 Fix Thresholds not being calculated for new balances 2014-08-06 01:45:32 +02:00
DanB
514df9a489 Fix nil pointer on timespans which are not paid 2014-08-05 23:24:20 +02:00
DanB
0a6f0ab0c7 Fixups osips_event tutorial 2014-08-05 22:41:56 +02:00
DanB
c0b18e3ae6 Config files for cgrates in osips_event tutorial 2014-08-05 19:07:06 +02:00
DanB
48d5866ea1 Adding opensips startup scripts for osips_event tutorial 2014-08-05 18:59:10 +02:00
DanB
e9ba47beab Individual configuration in sm/freeswitch for capturing extra fields 2014-08-05 18:17:10 +02:00
DanB
99eb85f8bc Merge branch 'master' of https://github.com/cgrates/cgrates 2014-08-05 16:03:20 +02:00
DanB
71a932e343 FS Event parsing cgr_category instead of cgr_tor, zero FS configuration support for passive billing scenarios 2014-08-05 16:03:02 +02:00
DanB
33e3f3fd66 Sql Flush command implementation to be used in tests, local tests for tutorial data 2014-08-05 01:09:05 +02:00
DanB
bb9e77c688 Fix cgr-tester.py script 2014-08-04 14:57:42 +02:00
DanB
ac3bbfe5a2 Fix tester 2014-08-04 14:33:12 +02:00
DanB
6e94842539 Fix in CgrTester to properly reflect TOR and Category 2014-08-04 13:48:22 +02:00
DanB
b28b8a60dd Dynamic setup_time on cdrs used in CdrStats local tests 2014-08-04 13:26:18 +02:00
DanB
88fdcbfca8 Updating sample osips_event with local addresses used in test 2014-08-04 13:03:14 +02:00
DanB
edb783ab39 Adding osips_event tutorial configurations 2014-08-03 16:55:08 +02:00
DanB
90fa1465ac Fixup concurrency on mediation runs and stats, improving tutorial CdrStats files 2014-08-03 16:04:43 +02:00
DanB
455379ef97 cgr-loader information formated with tab for verbose mode, adding data/tariffplans/tutorial folder to use as common tutorial tariff plan data 2014-08-02 22:24:39 +02:00
DanB
a48fa90dd8 Fix balance thresholds not being executed 2014-08-02 21:13:11 +02:00
DanB
a290a6aa6e cgr-loader to automatically reload cdrstats data 2014-08-02 18:30:22 +02:00
DanB
795a799ac2 Sample tariffplan for CDRStats 2014-08-02 17:19:57 +02:00
DanB
f2efe6fe9a Local tests for CDRStatsV1 2014-08-02 17:19:20 +02:00
Radu Ioan Fericean
4d78c0a63b more tests on stats 2014-08-02 17:51:12 +03:00
Radu Ioan Fericean
97c4cf5bd6 test for stats RPC methods 2014-08-02 15:33:17 +03:00
DanB
bcf4c3d08e StoreDisable implementation for both CDRS and Mediator 2014-08-02 13:58:15 +02:00
DanB
f72568054b Merge branch 'master' of https://github.com/cgrates/cgrates 2014-08-02 13:27:44 +02:00
DanB
cabcd67559 Config options to disable storing of the CDRs in CDRS and Mediator 2014-08-02 13:27:33 +02:00
Radu Ioan Fericean
a6b82806fd console now can receive params with array value 2014-08-02 13:35:57 +03:00
DanB
7c34c36f6f Merge branch 'master' of https://github.com/cgrates/cgrates 2014-08-01 21:07:40 +02:00
Radu Ioan Fericean
f8bb5d4d66 fix trigger execution 2014-08-01 22:06:47 +03:00
DanB
bdc127b76d Merge branch 'master' of https://github.com/cgrates/cgrates 2014-08-01 21:03:45 +02:00
Radu Ioan Fericean
909ceb2759 compilation erro fix 2014-08-01 22:01:33 +03:00
DanB
5a080846e7 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-08-01 20:59:43 +02:00
Radu Ioan Fericean
9adebc6b10 fixed trigger selection 2014-08-01 21:58:56 +03:00
DanB
09acd19fb7 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-08-01 20:47:15 +02:00
Radu Ioan Fericean
dd08456d13 keep *default queue on reload and revive rounding 2014-08-01 21:43:01 +03:00
DanB
b29abe35a4 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-08-01 20:36:03 +02:00
Radu Ioan Fericean
b86a8c77ba no more rounding 2014-08-01 21:35:43 +03:00
DanB
9feb8c47db Merge branch 'master' of https://github.com/cgrates/cgrates 2014-08-01 20:26:03 +02:00
DanB
c63a9a2221 Fix cdrstats api 2014-08-01 20:25:55 +02:00
Radu Ioan Fericean
5496a0eae2 better update queues 2014-08-01 21:25:39 +03:00
DanB
2411534ede Merge branch 'master' of https://github.com/cgrates/cgrates 2014-08-01 20:14:54 +02:00
Radu Ioan Fericean
3fcf8716e0 check for len ==0 instead of nil 2014-08-01 21:13:41 +03:00
DanB
146ef48435 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-08-01 20:05:17 +02:00
Radu Ioan Fericean
aade719f27 fix build error 2014-08-01 21:05:01 +03:00
DanB
25d0715a70 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-08-01 20:04:25 +02:00
Radu Ioan Fericean
7cbf5c2b56 initialize queue ids slice 2014-08-01 21:03:31 +03:00
DanB
2a953fcc33 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-08-01 20:01:43 +02:00
Radu Ioan Fericean
d07d772013 premature optimization 2014-08-01 20:57:11 +03:00
DanB
7a9d28d8f5 Fix conflicts 2014-08-01 19:29:48 +02:00
DanB
3a09524aa0 Fix Reset command 2014-08-01 19:28:54 +02:00
Radu Ioan Fericean
0b7fcf2167 first draft for reload and reset queues 2014-08-01 20:27:55 +03:00
Radu Ioan Fericean
a1e562e248 Added ResetQueue and cleaned cdrs on queue conf update 2014-08-01 20:17:02 +03:00
DanB
39379bf64a CDRStats.ResetQueues API Command 2014-08-01 19:03:53 +02:00
Radu Ioan Fericean
6dfaca4443 added ReloadQueues method 2014-08-01 19:47:16 +03:00
DanB
452b794f6d CDRStats.ReloadStatsQueues API method 2014-08-01 18:38:42 +02:00
Radu Ioan Fericean
605fde865a moved stats from accounting to rating db 2014-08-01 18:57:04 +03:00
Radu Ioan Fericean
6634751d7a cdrstats_metrics and cdrstats_queueids console commands 2014-08-01 18:47:52 +03:00
DanB
8d1be00962 Fixes local_tests 2014-08-01 17:30:22 +02:00
Radu Ioan Fericean
a096d97d94 added GetQueueIds and expire at GetValues 2014-08-01 17:59:13 +03:00
Radu Ioan Fericean
1574de1d38 some cache loking fixes 2014-08-01 17:59:13 +03:00
DanB
3e0e30f2cc Calculate stats after mediation storing the results in stordb 2014-08-01 15:36:37 +02:00
DanB
97977e98cf Merge branch 'master' of https://github.com/cgrates/cgrates 2014-08-01 13:38:02 +02:00
Radu Ioan Fericean
2527ad2970 fix hang on empty metrics 2014-08-01 14:37:16 +03:00
DanB
4779842574 Fix autodetection of IP subscribed to opensips 2014-08-01 13:16:48 +02:00
DanB
6092df2f76 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-08-01 12:56:14 +02:00
DanB
ba3eb3dda4 CdrStatsConfig with defaults 2014-08-01 12:52:43 +02:00
Radu Ioan Fericean
9e6d6a7b07 check for session manager initialized when shutdown 2014-08-01 13:00:40 +03:00
Radu Ioan Fericean
0033f400b2 fix get cdrstats from redis and a small rpc problem 2014-08-01 12:52:14 +03:00
Radu Ioan Fericean
8b77f457d2 initial length for queue map 2014-08-01 12:31:00 +03:00
DanB
63eafcab20 Adding OpenSIPS sample config for CDRStats 2014-08-01 11:27:18 +02:00
Radu Ioan Fericean
fdde40d312 fix for nil pointer in UpdateQueues 2014-08-01 12:20:37 +03:00
DanB
eef3a43016 Instantiating stats queue out of default config 2014-08-01 10:49:45 +02:00
Radu Ioan Fericean
8c54ad63e7 compilation fix for tpimporter 2014-07-31 22:57:47 +03:00
DanB
7ed24141b6 Default CdrStatsInstance configuration parsing 2014-07-31 19:32:54 +02:00
DanB
ac9b244eed Merge branch 'master' of https://github.com/cgrates/cgrates 2014-07-31 17:54:57 +02:00
DanB
b57fd3b09a Adding cdrstats config options 2014-07-31 17:54:27 +02:00
Radu Ioan Fericean
6d03010858 fixed triigers for tpimporter 2014-07-31 17:40:54 +03:00
DanB
67d6bdb4d8 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-07-31 15:28:45 +02:00
DanB
ed098e4665 Adding apier.CDRStatsV1 type and config.CdrStatsConfig 2014-07-31 15:28:42 +02:00
Radu Ioan Fericean
d6ede3560a more refactorings 2014-07-31 15:05:35 +03:00
Radu Ioan Fericean
d29512de02 moved AcceptCDR method into CdrStats 2014-07-31 15:00:56 +03:00
Radu Ioan Fericean
ebc972ed41 better stats methods 2014-07-31 14:55:47 +03:00
Radu Ioan Fericean
98cdbb6dc0 update cdr stats on engine start 2014-07-31 13:17:01 +03:00
Radu Ioan Fericean
26b1e8685b added all triggers to csv test 2014-07-30 20:15:12 +03:00
Radu Ioan Fericean
5400b2f44b rating subject instead of rate subject 2014-07-30 19:59:41 +03:00
Radu Ioan Fericean
1ae632ba80 5d fix 2014-07-30 19:58:05 +03:00
Radu Ioan Fericean
bbb116b699 fix for set tp triggers 2014-07-30 19:54:39 +03:00
Radu Ioan Fericean
ee64e5a0df fixed Scan 2014-07-30 19:36:57 +03:00
Radu Ioan Fericean
b2100a354a removed duplicate column 2014-07-30 19:32:13 +03:00
Radu Ioan Fericean
9c2925894b renamed column 2014-07-30 19:29:58 +03:00
Radu Ioan Fericean
bd6ed7e66a sql syntax fix 2014-07-30 19:27:44 +03:00
Radu Ioan Fericean
f86ab09580 initialize dests map 2014-07-30 19:23:28 +03:00
Radu Ioan Fericean
2250d9a2cc regxsp++ 2014-07-30 19:18:59 +03:00
Radu Ioan Fericean
2c72e2e7c0 more regexp fixes 2014-07-30 19:14:18 +03:00
Radu Ioan Fericean
1474516d9a fixed tarrif plans 2014-07-30 19:06:24 +03:00
Radu Ioan Fericean
6742345026 Merge branch 'master' into stats 2014-07-30 18:42:54 +03:00
Radu Ioan Fericean
91ac0fd3f2 more fixes for action trigger db load 2014-07-30 18:41:45 +03:00
DanB
7bff0db7d2 Small fixup sample opensips.cfg 2014-07-30 17:34:24 +02:00
DanB
7abf8cb12c SM-OpenSIPS with AUTHORIZATION for pseudoprepaid support implementation, sample opensips.cfg configuration 2014-07-30 17:28:59 +02:00
Radu Ioan Fericean
9c4d3439bb more triggerx regexp fixes, started load db 2014-07-30 17:03:14 +03:00
Radu Ioan Fericean
b012922272 test for cdr stats csv load 2014-07-30 14:23:31 +03:00
Radu Ioan Fericean
8342a00fcb added rated subject and account to cdrstats 2014-07-29 21:32:29 +03:00
Radu Ioan Fericean
8ee8962328 renamed QueuedItems to QueueLenth 2014-07-29 17:14:05 +03:00
DanB
1f28e2bc2c Fix OpenSIPS Event time parsing 2014-07-29 12:51:48 +02:00
DanB
0cc9b0caff Verbose test 2014-07-29 12:00:31 +02:00
Radu Ioan Fericean
72d38127f9 added one more csv file 2014-07-29 12:53:48 +03:00
Radu Ioan Fericean
f2da780893 compilation fix 2014-07-29 12:48:56 +03:00
Radu Ioan Fericean
4ea9d8f35a Merge branch 'master' into stats
Conflicts:
	apier/mediator.go
	cmd/cgr-engine/cgr-engine.go
	config/config.go
	config/config_test.go
	config/test_data.txt
	engine/cdrs.go
	engine/mediator.go
	general_tests/fsevcorelate_test.go
	test.sh
	utils/apitpdata.go
	utils/consts.go
2014-07-29 12:32:03 +03:00
DanB
2bab5f0585 OsipsDagram as part of packaging 2014-07-28 20:06:38 +02:00
DanB
fc546fe6b6 Adding FSMinDurLowBalance, FSLowBalanceAnnFile,FSEmptyBalanceContext, FSEmptyBalanceAnnFile and logic behind to support playing annoucements and transfers before disconnecting prepaid calls within FreeSWITCH SessionManager 2014-07-28 20:02:51 +02:00
DanB
7fff0b2aad Merge branch 'OsipsSM' 2014-07-28 18:15:05 +02:00
DanB
a541d32642 Adding SuppressCgrIds parameter on CdrExporter to prevent CgrIds reporting in replies 2014-07-28 16:11:46 +02:00
DanB
1d1aa5d3e1 Adding SupressCgrIds option to disable verbose reporting of Ids exported/not exported 2014-07-28 16:05:32 +02:00
DanB
9b2b3d2c94 OpenSIPS Events autosubscribe from SM 2014-07-28 16:01:26 +02:00
DanB
0f6e8540ef SM-OpenSIPS: configuration options in .cfg file 2014-07-27 12:24:28 +02:00
DanB
9cfe1a9864 Method to generate NewOsipsEvent 2014-07-27 11:28:36 +02:00
DanB
6cb79b5585 Connection to CDRS component and first CDR from OpenSIPS processed 2014-07-27 10:30:20 +02:00
DanB
dcb4fa77e0 Adding CdrHost to StoredCdr out of OsipsEvent 2014-07-27 10:21:25 +02:00
DanB
96d1775dd1 Adding AsStoredCdr method 2014-07-26 17:05:12 +02:00
DanB
a1d96d805e Initial SM-OpenSIPS and OpenSIPSEvent parsing with tests 2014-07-26 14:23:36 +02:00
DanB
e93b8a6c6f engine.Responder with ProcessCdr method, moved cdrs and mediator to engine 2014-07-25 17:44:50 +02:00
DanB
19e994ca8a Adding CDRSV1.ProcessCdr API to feed cdrs via RPC 2014-07-25 12:25:23 +02:00
DanB
4f5894b27e Cdrc now supports multiple field sources 2014-07-24 17:32:46 +02:00
DanB
36282cbc17 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-07-23 20:51:16 +02:00
DanB
f092d517df MediatorV1.RateCdrs with more filter options, renaming cdr arguments to reflect plurals properly 2014-07-23 19:53:52 +02:00
Radu Ioan Fericean
1091f58297 cdr stats csv load (without tests) 2014-07-22 22:10:39 +03:00
Radu Ioan Fericean
8fe59a2a3f Closed bracket 2014-07-22 10:30:15 +03:00
Radu Ioan Fericean
38b3dad5b0 Added webchat 2014-07-22 10:26:58 +03:00
Radu Ioan Fericean
c58684f99e removed unused rpc parameter 2014-07-22 10:14:08 +03:00
DanB
48bbe1af89 FS SessionManager - Timeout should be after connect, ticket #26 2014-07-21 19:21:25 +02:00
Radu Ioan Fericean
3dc0d44f4b compilation fix 2014-07-21 20:20:16 +03:00
Radu Ioan Fericean
8a2adb5e61 SendToStats parameter for mediator methods 2014-07-21 20:19:07 +03:00
Radu Ioan Fericean
8ac8b65a95 cdrs stats RPC service and integration with cdrs/mediator 2014-07-21 20:00:58 +03:00
DanB
71464ce65e Fixup min_callduration being used by max_callduration parsing 2014-07-18 15:09:06 +02:00
DanB
fd532fb1c3 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-07-17 20:42:42 +02:00
DanB
5e75234ede Query filters for ratedAccount and ratedSubject 2014-07-17 20:29:13 +02:00
Radu Ioan Fericean
f6d0454a81 added reset filters for action trigger reset
+test
2014-07-17 19:42:03 +03:00
Radu Ioan Fericean
1289adc155 added reset filters for action trigger reset
+test
2014-07-17 19:40:08 +03:00
Radu Ioan Fericean
a5bf017085 test balance match action trigger 2014-07-17 19:16:15 +03:00
Radu Ioan Fericean
ca4988dd77 Merge branch 'master' into stats
Conflicts:
	engine/action_trigger.go
2014-07-17 19:01:43 +03:00
Radu Ioan Fericean
93ec058b19 added RatingSubject and SharedGroup to action trigger 2014-07-17 19:00:12 +03:00
DanB
ae78f8fba0 Adding FieldAsString method to extract RatedAccount and RatedSubject, tests 2014-07-17 16:15:25 +02:00
DanB
b8eb3fa513 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-07-17 14:27:47 +02:00
DanB
5430490ec5 Adding RatedAccount and RatedSubject inside StoredCdr 2014-07-17 14:27:06 +02:00
Radu Ioan Fericean
362f34fe21 added parse command 2014-07-17 14:11:13 +03:00
Radu Ioan Fericean
982f6e0864 faster lock unlock 2014-07-17 13:33:17 +03:00
Radu Ioan Fericean
a42015a461 renamed BalanceKey to AccountKey 2014-07-17 11:56:13 +03:00
Radu Ioan Fericean
25da31a301 updated apier for expiration date 2014-07-17 11:40:07 +03:00
Radu Ioan Fericean
cb9e69f5c9 Merge branch 'master' into stats
Conflicts:
	engine/action_trigger.go
2014-07-17 11:16:24 +03:00
DanB
62d1a31290 Removing unique key in cdrs_extra since it only slows down INSERTs 2014-07-16 11:50:43 +02:00
DanB
3c4a247052 Adding min_callduration option in session manager 2014-07-16 11:34:51 +02:00
DanB
c08fc13629 Removing Type out of CgrXmlConfiguration 2014-07-16 11:14:45 +02:00
DanB
a449194b62 CdrExporter with configurable field separator, counters for sms and data usage 2014-07-16 10:56:56 +02:00
DanB
5a1994a943 Apier.AddTriggeredAction receiving now BalanceWeight and ExpiryTime 2014-07-16 09:36:15 +02:00
DanB
c92affde6d Merge branch 'master' of https://github.com/cgrates/cgrates 2014-07-15 19:47:01 +02:00
DanB
a722d3242b Concurent import for CDR files 2014-07-15 19:46:53 +02:00
Radu Ioan Fericean
8aa5540d23 action's execute now get stats queue 2014-07-15 18:33:46 +03:00
Radu Ioan Fericean
e6a6910a46 removed cdrstats dir 2014-07-15 16:38:54 +03:00
Radu Ioan Fericean
84dffb963a Merge branch 'master' into stats
Conflicts:
	cmd/cgr-engine/cgr-engine.go
	engine/action_trigger.go
	utils/rsrfield.go
2014-07-15 16:37:23 +03:00
Radu Ioan Fericean
c44287bee0 quick fix for compile error 2014-07-15 16:16:45 +03:00
Radu Ioan Fericean
8023de5f24 action trigger balance weight and exp time
also execute action trigger only on dirty balances
2014-07-15 16:14:58 +03:00
Radu Ioan Fericean
26fac17b33 started stats triggers 2014-07-15 15:43:33 +03:00
DanB
430da9b714 CdrExporter does not longer make differences between .csv and .fwv formats handling, treating fields mandatory property also 2014-07-15 11:59:46 +02:00
Radu Ioan Fericean
77da8b0daf various stats tests 2014-07-14 14:41:53 +03:00
DanB
31a573fde4 RSRField parser to accept one letter replace templates 2014-07-11 18:22:50 +02:00
DanB
e14e28b31c Removing debug log 2014-07-11 18:12:51 +03:00
DanB
e09212354c Adding re-connecting rpc client 2014-07-11 18:12:51 +03:00
DanB
5daf99f875 DerivedCharging run filters are now chained 2014-07-11 18:12:51 +03:00
DanB
a62ab32bbc Merging old ParseRSRFields from config with new one in utils 2014-07-11 18:12:51 +03:00
DanB
8c23a91f05 Logic to handle multiple derived charging run filters inside mediator and session manager 2014-07-11 18:12:51 +03:00
DanB
4b77ca7acd Adding ev.PassesFieldFilter tests 2014-07-11 18:12:51 +03:00
DanB
6a91c174c1 fsevent.ParseEventValue tests refactored to cope with time differences on different build machines 2014-07-11 18:12:51 +03:00
DanB
8017af8aab Changed cgrid hash to build using SetupTime.UTC() 2014-07-11 18:12:51 +03:00
DanB
50750e2dc2 Small test fix 2014-07-11 18:12:50 +03:00
DanB
511fcfd8f5 Adding ParseRSRFields for list of RSRFields, modifying NewRSRField static format to add suffix / 2014-07-11 18:12:50 +03:00
DanB
14c613150f StoredCdr.PassesFieldFilter with grouping in regexp rules, fsevent with PassesFieldFilter method for derived charging 2014-07-11 18:12:50 +03:00
DanB
b439ff9f92 Removing debug log 2014-07-11 18:12:50 +03:00
DanB
4ea3ac49da Allow derived charging regexp filters with empty templates 2014-07-11 18:12:50 +03:00
DanB
10374448e6 Disabling derived charging filter in session manager until better implementation 2014-07-11 18:12:50 +03:00
DanB
d1807386c6 Disabling debug message for derived charging filter in mediator 2014-07-11 18:12:50 +03:00
DanB
28c27c8863 RegexpSearchReplace rule has now Matched field to confirm matching, DerivedChargers has now Regexp support in filters 2014-07-11 18:12:50 +03:00
DanB
57110e81c8 Loader for derived charging filters, using derived chargers filters within mediator and session manager, filter implementation inside StoredCdr 2014-07-11 18:12:50 +03:00
DanB
c904343280 RunFilter in DerivedChargers, ParseEventValue accepting RSRField in FreeSWITCH event 2014-07-11 18:12:49 +03:00
DanB
392071da85 Cdrc to accept static fields in case of .csv files 2014-07-11 18:12:49 +03:00
DanB
bb102e65c2 Derived charging - fix nil pointer dereference in case of empty fields on fork cdr, cdrexporter correctly returning now empty file path in case of no cdrs to be exported 2014-07-11 18:12:49 +03:00
DanB
e9da9fd565 Fix CDRExporter to avoid writing empty fwv files, cgr-loader now orders rating recache in case of flushdb option used 2014-07-11 18:12:49 +03:00
DanB
c8c52842cb Removing debug log 2014-07-11 17:00:19 +02:00
DanB
192ce121de Adding re-connecting rpc client 2014-07-11 16:40:38 +02:00
DanB
d47e1bf53b DerivedCharging run filters are now chained 2014-07-11 10:28:01 +02:00
DanB
e808168d52 Merging old ParseRSRFields from config with new one in utils 2014-07-09 17:49:25 +02:00
DanB
22670e12a8 Logic to handle multiple derived charging run filters inside mediator and session manager 2014-07-09 15:21:42 +02:00
DanB
0986c1371b Adding ev.PassesFieldFilter tests 2014-07-09 13:39:57 +02:00
DanB
3a8d78e798 fsevent.ParseEventValue tests refactored to cope with time differences on different build machines 2014-07-09 12:57:06 +02:00
DanB
b5f26c00b8 Changed cgrid hash to build using SetupTime.UTC() 2014-07-09 12:31:45 +02:00
DanB
090f7706e8 Small test fix 2014-07-09 11:39:10 +02:00
DanB
6d00cce055 Adding ParseRSRFields for list of RSRFields, modifying NewRSRField static format to add suffix / 2014-07-08 18:55:37 +02:00
DanB
7bdd49b649 StoredCdr.PassesFieldFilter with grouping in regexp rules, fsevent with PassesFieldFilter method for derived charging 2014-07-08 14:32:20 +02:00
DanB
03fa289a8d Removing debug log 2014-07-07 19:26:59 +02:00
DanB
f77e6f03f3 Allow derived charging regexp filters with empty templates 2014-07-07 19:22:43 +02:00
DanB
a13ebf828c Disabling derived charging filter in session manager until better implementation 2014-07-07 18:17:09 +02:00
DanB
362d2d4b22 Disabling debug message for derived charging filter in mediator 2014-07-07 18:16:21 +02:00
DanB
e96251aba3 RegexpSearchReplace rule has now Matched field to confirm matching, DerivedChargers has now Regexp support in filters 2014-07-07 17:48:59 +02:00
DanB
31b421c755 Loader for derived charging filters, using derived chargers filters within mediator and session manager, filter implementation inside StoredCdr 2014-07-07 11:34:23 +02:00
DanB
9b37e97cc2 RunFilter in DerivedChargers, ParseEventValue accepting RSRField in FreeSWITCH event 2014-07-04 20:05:27 +02:00
DanB
a014ef8401 Cdrc to accept static fields in case of .csv files 2014-07-03 19:50:14 +02:00
DanB
a4b3d94431 Derived charging - fix nil pointer dereference in case of empty fields on fork cdr, cdrexporter correctly returning now empty file path in case of no cdrs to be exported 2014-07-02 19:39:19 +02:00
Radu Ioan Fericean
03dd27b8d8 added cdrstats to test.sh and started getstats 2014-07-01 23:33:08 +03:00
DanB
ff7f4cac09 Fix CDRExporter to avoid writing empty fwv files, cgr-loader now orders rating recache in case of flushdb option used 2014-07-01 18:27:12 +02:00
Radu Ioan Fericean
38877ca8f9 building new stats packages 2014-07-01 16:23:14 +03:00
Radu Ioan Fericean
2fef2248e1 started cdr stats 2014-07-01 15:16:11 +03:00
DanB
a25fc94d45 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-06-28 14:20:25 +02:00
DanB
4a21ed9684 CdrStatsConfig.ProcessedStats -> Metrics 2014-06-28 14:20:17 +02:00
Radu Ioan Fericean
8e505fdbd9 added a placeholder go file for 1.3 to compile 2014-06-24 17:53:00 +03:00
Radu Ioan Fericean
bcb9479ee5 removed duplicated line 2014-06-24 17:52:24 +03:00
cgrates
fcecc823d0 added all supported go versions to travis 2014-06-22 10:12:07 +03:00
cgrates
704fae01ce Switched to go 1.3 for travis 2014-06-22 09:58:41 +03:00
Radu Ioan Fericean
1edd4f13d8 docker files for development and production testing 2014-06-21 16:16:46 +03:00
DanB
3d27154b91 callcost->cost_details 2014-06-19 13:36:49 +02:00
DanB
0f5b998c82 Fix cdrexporter processing the stats before built 2014-06-19 11:13:16 +02:00
DanB
056b9c6c61 Fixup Responder.GetDerivedCharging, adding localtests for both ApierV1.GetDerivedCharging and Responder.GetDerivedCharging 2014-06-17 12:51:18 +02:00
DanB
d3835067cf Session manager fixups for derived charging 2014-06-16 17:58:25 +02:00
DanB
222602dfff Postpaid calls no longer handled by session manager 2014-06-16 11:26:05 +02:00
DanB
bc5a80fe25 Adding *voice balance and derived charging inside fs_json tutorial, fixup loading of derived charging from csv 2014-06-15 19:05:00 +02:00
DanB
c973ea99e2 Fixups CdrExporter, derived charging should not transfer but kill the session if errors onAnswer 2014-06-15 12:22:06 +02:00
DanB
ff714a1bc8 Small sql fixup on missing cdr_extra values 2014-06-12 11:18:21 +02:00
DanB
c8ce2ddce2 Adding datetime field to CdrExporter 2014-06-11 19:47:25 +02:00
DanB
fe06ede121 Failed charging test 2014-06-07 13:36:56 +02:00
DanB
196978bcec Adding combimed cdrexporter fields, filter parameter for cdrfields 2014-06-07 12:49:12 +02:00
DanB
cc585cd86e Adding filter value to cdre config 2014-06-06 12:30:54 +02:00
DanB
dac4300726 Adding sleep parameter to CdrStats triggers 2014-06-06 10:50:53 +02:00
DanB
6b1a67d4f5 Expanding CallStatsConfig 2014-06-05 20:04:44 +02:00
DanB
c4a463606a Add RatedCdrs parameter in CdrStatsConfig 2014-06-05 13:01:59 +02:00
DanB
4af3af33c5 Initial CdrStatsConfig sample 2014-06-05 12:58:44 +02:00
DanB
4545ca1a30 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-06-05 11:06:51 +02:00
DanB
f9f994cd7e HttpSkipTlsVerify for http client 2014-06-05 11:02:17 +02:00
Radu Ioan Fericean
3d99f917f5 renamed balance_add to balance_set
now it accepts negative values
2014-06-04 18:43:29 +03:00
Radu Ioan Fericean
b56356486f balance debit return type 2014-06-04 15:25:23 +03:00
DanB
7bd66530fe Fixup balance_debit command, cdrexporter supporting http_post field values 2014-06-04 13:46:53 +02:00
DanB
8e7eb8233f Fixup default width parameters for cdre config 2014-06-04 12:21:20 +02:00
DanB
f436346873 Refactored CDRExporter to merge common exports into one mechanism, added CdreConfig type to collect configuration for CDRE from more sources 2014-06-03 19:05:36 +02:00
DanB
c063bc2a21 Adding utils.HttpJsonPost client 2014-05-28 19:33:47 +02:00
DanB
1e92a6795f Merge branch 'master' of https://github.com/cgrates/cgrates 2014-05-28 12:54:25 +02:00
DanB
18629002a6 StoredCdr.AsCgrCdrOut() with tests 2014-05-28 12:54:03 +02:00
Radu Ioan Fericean
b62d7ba52d fallback on *any destination 2014-05-28 13:53:49 +03:00
DanB
7b2a452c4f Correcting sms test 2014-05-28 10:17:49 +02:00
DanB
e15a08d633 Adding SMS Charging test 2014-05-28 10:12:38 +02:00
DanB
4f95f56284 Usage stored as decimal in MySQL, more tests and sample tariff plan data 2014-05-28 09:36:16 +02:00
DanB
42e9719fed DurationMangle is now bidirectional, cdr exporter properly formating duration 2014-05-26 14:16:35 +02:00
DanB
31881ea41c StoredCdr.FormatUsage now considers Data, StoredCdr.MangleDataUsage to properly export CDR objects 2014-05-26 12:58:00 +02:00
DanB
e6aaf24a14 Small rule fixup regexp + prefixing E164 numbers 2014-05-25 19:35:41 +02:00
DanB
00248b16dc General tests and configs for multiple cdrc imports 2014-05-25 16:23:05 +02:00
DanB
dbf6379818 Adding TOR in cgrates.cfg, fixing tests 2014-05-25 12:23:57 +02:00
DanB
93fa37b20b Including TOR field in cdrc, tests fixups 2014-05-25 11:16:52 +02:00
DanB
9bf5f7611b xmlcdrc with setDefaults(), modifying default order of imported and exported cdrs 2014-05-23 21:44:33 +02:00
DanB
5fe65f203c Adding compressed date string parsing, sample multiple cdrc with fw exporter as .xml 2014-05-21 20:10:40 +02:00
DanB
ad4fedc197 Fork cdrc over multiple folders based on xml configuration 2014-05-21 17:39:25 +02:00
DanB
201b7e7677 Removing CdrcPostMethod, default roundDecimals to 10 2014-05-21 10:13:02 +02:00
DanB
f0b2f240b1 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-05-20 13:47:25 +02:00
DanB
cb33f01695 Adding xml configuration for cdrc 2014-05-20 13:41:02 +02:00
Radu Ioan Fericean
bbe64e3e30 fix for data interval splitting (thanks DanB) 2014-05-19 23:53:22 +03:00
DanB
5aef6595a2 Fixup local tests regarding AttrGetAccount 2014-05-19 18:03:40 +02:00
DanB
1ad4068d73 Fixup CdrServer receiving nanoseconds as usage, more tests 2014-05-19 17:50:31 +02:00
DanB
d5f6df9473 Destination optional when forking Data Cdrs, adding mediation local tests for rated data charging 2014-05-19 10:46:12 +02:00
DanB
66d9377c53 DerivedChargers DurationField -> UsageField 2014-05-19 10:21:20 +02:00
DanB
79237637e1 cgrates.cfg _duration fields -> _usage one 2014-05-19 09:59:50 +02:00
DanB
e8690fc23e StoredCdr.Duration -> StoredCdr.Usage 2014-05-19 09:24:46 +02:00
DanB
aaeac3329a CgrCdr duration->usage to comply with data CDRs 2014-05-18 22:04:16 +02:00
Radu Ioan Fericean
129ca0cd93 *any date fixes 2014-05-18 20:21:29 +03:00
DanB
9bea2fa7d7 Adding datacost console command, test getDataCost 2014-05-18 18:13:59 +02:00
DanB
4d33d7cf81 DestinationRates accepting * any as destination id, adding sample data plan to prepaid1centpsec sample tariff plan 2014-05-18 14:54:34 +02:00
DanB
2acd4377e9 apier/costs.go file 2014-05-16 20:09:00 +02:00
DanB
8c29d041f1 Initial ApierV1.GetDataCosts 2014-05-16 20:08:34 +02:00
DanB
a90356d62c Fixup re-rating not using cost -1 2014-05-15 16:00:34 +02:00
DanB
040430bc3b Quick fix rated_cdrs usage format 2014-05-15 12:35:15 +02:00
DanB
d0d49481a2 ApierV1.GetCdrs method, CDR fields now exported from rated_cdrs table to reflect derived charging, duration layout, added upgrade script for mysql schema 2014-05-15 12:13:03 +02:00
DanB
d6299cd08d Merge branch 'master' of https://github.com/cgrates/cgrates 2014-05-14 13:03:42 +02:00
DanB
65b7f19871 Adding GetActions API with tests 2014-05-14 12:46:37 +02:00
Radu Ioan Fericean
7f747c4529 test for action timing next start date 2014-05-14 12:24:24 +03:00
DanB
ec5b8711ad Fixup Tor->Category within ExportCdrsToFile API 2014-05-13 20:22:33 +02:00
DanB
dd4d9ab87e Merge branch 'master' of https://github.com/cgrates/cgrates 2014-05-13 19:21:19 +02:00
DanB
4f4f3b1108 ApierV1.GetScheduledActions with filters 2014-05-13 19:21:03 +02:00
Radu Ioan Fericean
6536465e1f check for obsolete tasks 2014-05-13 19:30:04 +03:00
DanB
c3f8521ad4 Get Aliases now from cache only 2014-05-13 11:58:50 +02:00
DanB
e157966af2 Small fixups on Alias keys 2014-05-12 19:12:42 +02:00
DanB
0011e417c2 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-05-12 19:03:14 +02:00
DanB
da6f9cad6a ApierV1 methods for add, get, remove aliases, local tests attached 2014-05-12 19:03:07 +02:00
Radu Ioan Fericean
ec66859d34 console results fix 2014-05-12 18:44:22 +03:00
Radu Ioan Fericean
0bfd104e12 add GetQueue method to scheduler 2014-05-12 16:01:47 +03:00
Radu Ioan Fericean
f8cd44f792 changed liner path 2014-05-12 13:00:40 +03:00
DanB
79d654c2a0 Aliases now with multi-tenant support, ApierV1.GetRatingSubjectAliases, ApierV1.GetAccountAliases RPC commands implementation 2014-05-11 21:48:51 +02:00
DanB
6ffdb1633f TOR in exported CDRs 2014-05-11 13:46:19 +02:00
DanB
23e2db7332 RPC MediatorV1 moved to apier package for centralized documentation 2014-05-11 13:20:25 +02:00
DanB
7bc0003fcf GetStoredCdrs taking TORs as filter 2014-05-11 13:09:53 +02:00
DanB
b54a8d7dd7 Finished adding TOR field inside CDRs 2014-05-11 11:25:29 +02:00
DanB
370fc31db8 Adding TOR support inside CDRs 2014-05-09 19:24:38 +02:00
DanB
ed3ef16c09 DBCdrs: duration -> usage to cope with the *sms and *data TORs 2014-05-08 20:08:11 +02:00
Radu Ioan Fericean
2b8b4f4791 renamed *call_duration to *voice 2014-05-08 17:09:42 +03:00
DanB
a4c556a484 Removing stale .sql file 2014-05-08 10:23:27 +02:00
DanB
e893862f26 MySQL data structures refactoring, fixups rounding methods migration 2014-05-08 10:20:46 +02:00
Radu Ioan Fericean
c879c7a4d5 more fixes 2014-05-07 20:12:48 +03:00
Radu Ioan Fericean
6d0cbfe2aa local tests fixes for rounding movement 2014-05-07 20:02:02 +03:00
Radu Ioan Fericean
456039d0a6 vagrant ansible fixes 2014-05-07 19:55:38 +03:00
Radu Ioan Fericean
7a70b50bf4 loader db updates for rounding stuf movement 2014-05-07 15:31:54 +03:00
Radu Ioan Fericean
dc7330b3c9 moved rounding params from rates to destination rates 2014-05-07 15:00:02 +03:00
Radu Ioan Fericean
928adcfbf0 no more global rounding method 2014-05-07 15:00:02 +03:00
DanB
0ef211de7e Fixups mediation derived charging 2014-05-07 13:53:44 +02:00
DanB
9512291c49 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-05-07 12:42:19 +02:00
DanB
e74a98fead Finished internal CDR handling refactoring, fixups tor->category, ratingProfile definition changes, ActionTriggers with Recurrent property 2014-05-07 12:41:57 +02:00
DanB
f466fcbb6a Proper ratingprofile order 2014-05-06 20:26:33 +02:00
DanB
f7abbacfe5 Refactoring CDRs to support RSRFields 2014-05-05 20:14:40 +02:00
Radu Ioan Fericean
f45833edeb small fix 2014-04-30 19:19:44 +03:00
Radu Ioan Fericean
7bda45fcce added max cost support 2014-04-29 15:24:54 +03:00
Radu Ioan Fericean
95a8e8a217 watch for max rate for unit costs as well 2014-04-29 14:33:28 +03:00
Radu Ioan Fericean
ede467c899 Merge branch 'lcr'
Conflicts:
	engine/calldesc.go
	engine/handler_derivedcharging.go
	mediator/mediator.go
	utils/apitpdata.go
2014-04-29 14:20:15 +03:00
Radu Ioan Fericean
fcdc0c03c7 Merge branch 'data'
Conflicts:
	engine/calldesc.go
	mediator/mediator.go
	sessionmanager/fssessionmanager.go
	utils/storedcdr.go
2014-04-29 14:13:48 +03:00
Radu Ioan Fericean
b5af601163 added max rate 2014-04-29 13:54:51 +03:00
DanB
7bd69607e4 Add rif/liner to update_external_libs.sh for packaging 2014-04-28 17:58:41 +02:00
DanB
0ebbb66dff Merge branch 'master' of https://github.com/cgrates/cgrates 2014-04-28 17:11:43 +02:00
DanB
5e114192b6 Fixup APIs DerivedCharging, adding more tests 2014-04-28 17:11:37 +02:00
Radu Ioan Fericean
e5f66a8f4b only delete if cache key exists 2014-04-28 16:37:39 +03:00
Radu Ioan Fericean
9a63438577 added lock for counters flush 2014-04-28 13:33:37 +03:00
DanB
45e798ce5d Merge branch 'master' of https://github.com/cgrates/cgrates 2014-04-28 12:33:11 +02:00
DanB
5aebe92788 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-04-28 12:06:47 +02:00
DanB
e39187eab9 TOR -> Category in relation with DerivedCharging 2014-04-28 12:06:42 +02:00
Radu Ioan Fericean
c9493005cd more cache fixes 2014-04-28 12:45:21 +03:00
Radu Ioan Fericean
21176f7c88 cache fixes 2014-04-28 12:26:27 +03:00
Radu Ioan Fericean
24a2436f81 faster cache counter and unified caches 2014-04-28 12:12:19 +03:00
Radu Ioan Fericean
6f6f4c8c47 package updating scripts 2014-04-28 02:11:14 +03:00
DanB
6497a09ae5 RSRules chain implementation 2014-04-27 21:39:52 +02:00
DanB
69f5921a8d Fix MediatorLocal test considering derived charging 2014-04-27 14:51:05 +02:00
Radu Ioan Fericean
5bd0816596 using cgrates own branch for liner 2014-04-27 15:24:40 +03:00
DanB
e01ad95465 cdr AsStoredCdr() -> ForkCdr(), fixup ForkCdr in StoredCdr type 2014-04-27 14:17:36 +02:00
DanB
35cb7483de CgrCdr.AsStoredCdr supporting now *default as field names 2014-04-27 09:59:08 +02:00
DanB
f0095dbcb3 RawCDR interface now properly returns duration 2014-04-27 09:19:30 +02:00
Radu Ioan Fericean
a365e90c1d switched to peterh liner and fixed commands 2014-04-27 00:41:34 +03:00
Radu Ioan Fericean
509e3cde45 refactoring fixses 2014-04-26 01:52:15 +03:00
Radu Ioan Fericean
255fb4ea0e cd Tor to TOR 2014-04-26 01:50:08 +03:00
Radu Ioan Fericean
9d0f252aad lcr load and torfield to categoryfield 2014-04-26 01:45:51 +03:00
Radu Ioan Fericean
f1731cccb7 lcr loading 2014-04-26 01:33:48 +03:00
Radu Ioan Fericean
393d9091b6 merge fixes 2014-04-25 22:35:43 +03:00
Radu Ioan Fericean
1d7d0b8078 Merge branch 'data' into lcr
Conflicts:
	apier/apier.go
	cmd/cgr-loader/cgr-loader.go
	engine/calldesc.go
	engine/loader_csv.go
	engine/loader_csv_test.go
	engine/storage_interface.go
	general_tests/ddazmbl1_test.go
	utils/apitpdata.go
2014-04-25 22:10:55 +03:00
Radu Ioan Fericean
402a1fbc68 Call descriptot Type is now Tor 2014-04-25 21:50:34 +03:00
Radu Ioan Fericean
12f91b009b changed TOR in mediator 2014-04-25 21:45:05 +03:00
Radu Ioan Fericean
48de7f3c30 Merge branch 'master' into refactor
Conflicts:
	sessionmanager/fssessionmanager.go
	sessionmanager/session.go
2014-04-25 21:42:48 +03:00
Radu Ioan Fericean
a95fef496b more CallDuration changes 2014-04-25 21:22:53 +03:00
Radu Ioan Fericean
25cb5be8c0 refactor CallDuration in DurationIndex and TOR in Category 2014-04-25 21:16:21 +03:00
Radu Ioan Fericean
6526e52603 rating profile order + fix general tests 2014-04-25 20:49:44 +03:00
Radu Ioan Fericean
bc94fe34d1 millisecond for scheduler 2014-04-25 20:36:40 +03:00
DanB
330fb7b894 Fixup duration parsing, adding some tests 2014-04-25 19:35:59 +02:00
Radu Ioan Fericean
4934daa6b9 modified loader for recurrent flag 2014-04-25 20:26:41 +03:00
Radu Ioan Fericean
99faa0c112 recurrent action trigger 2014-04-25 19:23:24 +03:00
Radu Ioan Fericean
4fdf80c633 renamed *call to *call_duration 2014-04-25 19:13:14 +03:00
Radu Ioan Fericean
575bcf2434 debit zero cost calls even for no credit 2014-04-25 17:16:59 +03:00
Radu Ioan Fericean
022e3a8c38 callcost to datacost conversion method 2014-04-25 14:12:17 +03:00
Radu Ioan Fericean
c790f59b58 initial tests for data rating 2014-04-25 13:00:51 +03:00
Radu Ioan Fericean
2013c16b0e small simplification 2014-04-25 10:38:13 +03:00
Radu Ioan Fericean
4265dff012 *minutes is now *call 2014-04-25 10:37:42 +03:00
DanB
b779f09d9d Merge branch 'master' of https://github.com/cgrates/cgrates 2014-04-25 09:32:28 +02:00
Radu Ioan Fericean
61e81ced27 small go vet fix 2014-04-25 01:20:38 +03:00
Radu Ioan Fericean
4ffc8555a1 new call descryptor type attribute 2014-04-24 22:42:14 +03:00
DanB
1953bf4233 Adding META_DEFAULT to FSevent parsing 2014-04-24 19:44:28 +02:00
Radu Ioan Fericean
42718617b9 data get cost 2014-04-24 20:01:20 +03:00
DanB
ddcd54db4b Integration of DerivedCharging within mediation 2014-04-24 18:33:18 +02:00
DanB
90ab47ade9 Session manager refactoring to work with derived charging 2014-04-24 13:39:52 +02:00
DanB
b1b3f4ab18 More derived charging infrastucture and tests 2014-04-24 10:14:02 +02:00
DanB
2a253892c5 Refactoring the code around derived charging to support integration with responder for internal requests, some of the copyright headers updated with new slogan 2014-04-23 20:48:43 +02:00
Radu Ioan Fericean
b4dbb1ca9e more time for the scheduler 2014-04-23 13:32:18 +03:00
Radu Ioan Fericean
251d504d40 fix for callcost out of bounds merge (thanks DigiDaz) 2014-04-23 13:24:41 +03:00
DanB
bba4a878f0 Refactoring configuration for DerivedCharging 2014-04-22 13:13:39 +02:00
Radu Ioan Fericean
2d042d65bf ignore empty commands (enter enter) 2014-04-21 19:12:38 +03:00
Radu Ioan Fericean
d3e5555b5c fix for rating subject shortucts 2014-04-21 12:03:15 +03:00
Radu Ioan Fericean
f8fec02ab5 fix for expiration time issue 2014-04-19 17:01:25 +03:00
Radu Ioan Fericean
83b7034b7f Merge branch 'master' into console-ng 2014-04-19 16:33:59 +03:00
Radu Ioan Fericean
efe9e19460 small fixes 2014-04-19 16:33:48 +03:00
Radu Ioan Fericean
1935d41b9d renamed commands 2014-04-19 16:06:53 +03:00
Radu Ioan Fericean
92d03eaf1a AddBalance expiration date accepting shortcuts 2014-04-19 12:37:22 +03:00
Radu Ioan Fericean
0e55acbfdb fixes 2014-04-19 12:19:44 +03:00
Radu Ioan Fericean
0122acced1 use string wrapper for string param commands 2014-04-19 11:48:05 +03:00
Radu Ioan Fericean
efc9240142 removed BalanceType from AttrGetAccount 2014-04-18 23:04:14 +03:00
Radu Ioan Fericean
bcb9582d6b more commands and help 2014-04-18 23:03:51 +03:00
DanB
94da5d9e74 Adding caching for DerivedCharging settings 2014-04-18 19:59:11 +02:00
DanB
05e4772f6d Fixup SM parse time bug discovered by DigiDaz, adding some more tests 2014-04-18 18:03:05 +02:00
DanB
4637c247fe DerivedChargers into their own files in config package 2014-04-18 16:50:15 +02:00
DanB
27e8c34fcb Fixup tests 2014-04-18 16:49:35 +02:00
Radu Ioan Fericean
6df17bf2e8 introducing command executer 2014-04-18 17:25:22 +03:00
DanB
57b4226e8a Sample DerivedChargers.csv 2014-04-18 14:43:16 +02:00
DanB
43039b063b Updating after merge 2014-04-18 14:42:52 +02:00
DanB
668352b860 Initial DerivedCharging loading 2014-04-18 14:39:59 +02:00
Radu Ioan Fericean
2da2294c0f add_balance 2014-04-17 16:59:17 +03:00
Radu Ioan Fericean
5f4580947a add_account and status 2014-04-17 16:32:20 +03:00
Radu Ioan Fericean
0192e5088f using rif's liner fork 2014-04-17 15:37:26 +03:00
Radu Ioan Fericean
4cc6d248e4 Merge branch 'master' into console-ng 2014-04-17 13:12:45 +03:00
Radu Ioan Fericean
e1c174bebb small fix 2014-04-17 13:10:43 +03:00
Radu Ioan Fericean
81b611f186 first (somehow) working version 2014-04-16 22:22:17 +03:00
DanB
d08b77fcb7 Adding TotalCost to export stats 2014-04-16 19:22:57 +02:00
DanB
341ead514e Fixup time parser to support 0 as timestamps sent by FreeSWITCH 2014-04-16 16:32:45 +02:00
DanB
0236553c87 Disabling multiprocessor support since benchmarks have shown a decrease of performance due to additional tasks management. To be reviewed in future Go versions when scheduler will be reconsidered 2014-04-16 10:57:29 +02:00
DanB
f1b639b04e Fixup responder trying to read nil in case of errors coming from above, adding some XML parsing tests 2014-04-16 10:51:27 +02:00
DanB
4e1776d6fd Discarding FS microseconds information, reverting to seconds since MySQL errors in older versions and default FS csv file uses seconds 2014-04-11 17:12:27 +02:00
DanB
5d9e7bae67 Adding more RSRTests 2014-04-10 19:12:27 +02:00
DanB
f2eade654c ExportDir as parameter to export api 2014-04-10 13:45:02 +02:00
Radu Ioan Fericean
df655e7751 handling *any destination id 2014-04-09 20:17:13 +03:00
Radu Ioan Fericean
60075616b4 first try for ga-beakon 2014-04-09 19:08:08 +03:00
DanB
b958ee38aa Merge branch 'master' of https://github.com/cgrates/cgrates 2014-04-09 17:38:35 +02:00
DanB
31138857ee Decreasing timer in case of general_tests 2014-04-09 17:37:03 +02:00
Radu Ioan Fericean
adc7c3d671 add ResetTriggeredAction api call 2014-04-09 18:27:36 +03:00
DanB
e303940d76 Adding tests on multiple balances: 0 monetary with chargeable minutes, missing monetary with chargeable minutes 2014-04-09 17:21:45 +02:00
DanB
5f65388e69 Fixup test.sh with new general_tests 2014-04-09 15:43:17 +02:00
DanB
5d29ce3d61 Changing charging_tests to general_tests to reflect more general tests inside 2014-04-09 15:40:17 +02:00
DanB
9fcf43f8b4 Corelate CgrIds between FS answer_event and FsJsonCdr 2014-04-09 12:23:56 +02:00
DanB
a7b71bae43 Fixup CgrId in FS-SM 2014-04-08 21:30:23 +02:00
Radu Ioan Fericean
c11e5ae7e7 seconds default "s" marker now optional in rates 2014-04-08 21:39:09 +03:00
Radu Ioan Fericean
d6f601e9ab use increments compression 2014-04-08 20:47:28 +03:00
Radu Ioan Fericean
fc76d891b7 first timespans compress test 2014-04-08 19:57:52 +03:00
DanB
a3fff42338 FsCgrId -> Sha1, making the CgrId even more uniquely by hashing it with setup time 2014-04-08 17:51:18 +02:00
DanB
b40c3d1653 Decreasing path in case of cdrexporter to /var/log/cgrates/cdre 2014-04-08 11:43:11 +02:00
DanB
9dcd7c0ef4 Adding OrderId to storedCdrs and using it in sql/GetStoredCdrs and exporters 2014-04-08 10:39:09 +02:00
Radu Ioan Fericean
62148b6d06 add increments compression
tests pending
2014-04-07 18:20:36 +03:00
Radu Ioan Fericean
1dca95a6d3 fix build 2014-04-07 13:54:46 +03:00
Radu Ioan Fericean
d624af2e94 Merge branch 'master' into lcr 2014-04-07 13:33:09 +03:00
Radu Ioan Fericean
6dd22fa847 zero cost fix (♥ DigiDaz) 2014-04-07 12:11:09 +03:00
Radu Ioan Fericean
2e6ee328c9 clean all balance precision, bugfix (thanks DigiDaz) 2014-04-06 23:48:55 +03:00
DanB
d5a8a2c292 Small bug fixup in action_timings possible reaching nil pointer error, adding charging_tests folder to improve testing mechanisms 2014-04-06 19:15:31 +02:00
Radu Ioan Fericean
7313e64362 rename RateSubject to RatingSubject 2014-04-06 12:58:21 +03:00
DanB
564efade3d Merge branch 'master' of https://github.com/cgrates/cgrates 2014-04-05 13:43:58 +02:00
DanB
8fd5c112b1 Adding Account to console debit_balance 2014-04-05 13:01:30 +02:00
DanB
c940d628a6 Fixup load TpRatingPlan in storage 2014-04-04 21:09:11 +02:00
Radu Ioan Fericean
71f296d656 test for DigiDaz regression 2014-04-04 16:51:02 +03:00
DanB
517bf7964b action enable/disable_user -> enable/disable_account, cgr-tester to use test data compatible with fs_json tutorial 2014-04-03 16:36:03 +02:00
Radu Ioan Fericean
834a25ae74 Merge branch 'master' into lcr 2014-04-01 20:50:05 +03:00
Radu Ioan Fericean
a03b25cc45 cleaned unused fields 2014-04-01 20:49:34 +03:00
DanB
e5dc9a2e56 Work around in exporter api to keep config defaults 2014-04-01 16:53:04 +02:00
DanB
ac86c6b60c Fix test of costShiftDigits 2014-04-01 16:13:11 +02:00
DanB
cd29a3360b Adding costDigitsShift formatting 2014-04-01 16:08:34 +02:00
DanB
eb333fcfc0 storage/GetStoredCdrs takes a higher number of filters for exports 2014-04-01 11:56:04 +02:00
DanB
3313e7c41b Fixup local test 2014-03-31 20:34:28 +02:00
DanB
ac1e9992bf Merge branch 'master' of https://github.com/cgrates/cgrates 2014-03-31 20:29:05 +02:00
DanB
cc0534aa8b Fixup masking in the fwv exporter 2014-03-31 20:27:12 +02:00
Radu Ioan Fericean
62c56ae26b stale destination prefix cleaning 2014-03-31 21:01:52 +03:00
Radu Ioan Fericean
a8e4dad5f0 removed rateing id from callcost and loader 2014-03-31 14:18:02 +03:00
Radu Ioan Fericean
8276ea73b7 removed amount related stuff from calldesc 2014-03-31 14:06:46 +03:00
Radu Ioan Fericean
cdd93e2161 Merge branch 'master' into lcr 2014-03-31 11:44:47 +03:00
DanB
46959aaa35 Adding mask destination functionality also in csv exporter 2014-03-29 16:37:51 +01:00
DanB
c96f48435b Increasing version to avoid confusion on features support 2014-03-29 14:04:27 +01:00
DanB
65d23a1eb5 Adding MaskDestination support in cdrexporter 2014-03-29 14:01:14 +01:00
DanB
9b1ba8b6c7 Fixup packaging rules, add tab 2014-03-28 20:37:38 +01:00
DanB
cbfcd1133d Correcting rules 2014-03-28 20:35:31 +01:00
DanB
52a5fb918e Correcting packaging to reflect cdre folder 2014-03-28 20:30:02 +01:00
DanB
8325c8e2e7 Fixups cdrexporter, export path now to cdre+ export type 2014-03-28 20:13:28 +01:00
DanB
d8516d2662 Cdre-FW: filler type adds automatically padding 2014-03-28 18:38:11 +01:00
DanB
d062a98938 Concurrency fix in engine for setting history server 2014-03-27 12:17:28 +01:00
Radu Ioan Fericean
7c37399757 small cleanup 2014-03-27 09:46:51 +02:00
Radu Ioan Fericean
611743d3c5 first lcr algo 2014-03-26 11:51:03 +02:00
DanB
c8f4118780 Correcting version number 2014-03-26 10:27:35 +01:00
DanB
fded831fad Bug fixup on loading extra fields configuration, better config testing 2014-03-26 10:26:32 +01:00
Radu Ioan Fericean
e20605471e compilation fixes 2014-03-25 21:41:11 +02:00
Radu Ioan Fericean
ee6b1cd74f add lcr strategy 2014-03-25 21:38:18 +02:00
DanB
24fda5b14b Changelog update 2014-03-25 17:25:15 +01:00
DanB
f3f6bb1e16 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-03-25 17:19:46 +01:00
DanB
9211c01d69 Fixup tutorial readmes 2014-03-25 17:18:09 +01:00
Radu Ioan Fericean
d95d842ede Merge branch 'master' into lcr 2014-03-25 18:17:47 +02:00
Radu Ioan Fericean
87481ed520 rename ActionTimings Tag and Id 2014-03-25 18:17:17 +02:00
DanB
5857e63823 Changing documentation index to rc4 2014-03-25 17:07:50 +01:00
DanB
9ebf2573b0 Adding console command: rem_cdrs 2014-03-25 17:05:00 +01:00
Radu Ioan Fericean
8987b11cbd added lcr storage methods 2014-03-25 18:01:49 +02:00
DanB
e67db4a434 Tests fixup for exporter 2014-03-25 16:42:45 +01:00
DanB
375bf8c0dd CdrExporter returns stats for successfuly and unsuccessfuly exported CDRs 2014-03-25 16:00:33 +01:00
DanB
6acfa22a04 Adding localtests for RemStoredCdrs 2014-03-25 13:40:06 +01:00
DanB
29e3bd137b Fixup build, removed removeFromDb parameter in cdr_export console command 2014-03-25 13:30:39 +01:00
Radu Ioan Fericean
26b9590fa0 Merge branch 'master' into lcr 2014-03-25 09:59:37 +02:00
DanB
f390281f41 API for CdrExporter fixed_width, RemCdr separated from exporter due to buggy scenario discovered 2014-03-24 22:23:03 +01:00
DanB
881db9c1c4 Adding required to fixed_width filter, adding export of the header and trailer, tests 2014-03-24 16:50:57 +01:00
DanB
def252d153 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-03-23 21:40:17 +01:00
DanB
2bc4e3fb7e Basic implementation of fixed_width cdr export content 2014-03-23 21:17:48 +01:00
Radu Ioan Fericean
e381265ace save dirty balances at the end of debit 2014-03-23 20:21:05 +02:00
DanB
15f2a2cd82 Adding more properties to XmlCdrFields config 2014-03-23 09:14:43 +01:00
DanB
4bb3e745f1 Adding config xml template for exporter with fixed width CDRs together with tests 2014-03-22 22:16:59 +01:00
DanB
4045d022ae Merge branch 'master' of https://github.com/cgrates/cgrates 2014-03-22 16:51:03 +01:00
DanB
5e06cafd2f Adding rating aliases, account aliases, shared groups to cache stats struct, improving fs_json tutorial tests and documentation 2014-03-22 16:50:25 +01:00
DanB
a93ecfc049 Adding initial xmlconfig structure with tests 2014-03-22 16:49:06 +01:00
Radu Ioan Fericean
5d0e6b4fee removed superflous if 2014-03-21 18:15:46 +02:00
Radu Ioan Fericean
5de1a83bf5 process all balances on debit (own+shared) 2014-03-21 18:01:28 +02:00
Radu Ioan Fericean
c6de153685 more tests and disabled shared gropup caching 2014-03-20 19:05:31 +02:00
Radu Ioan Fericean
22adfb552f debit from shared group empty balances 2014-03-20 17:27:29 +02:00
Radu Ioan Fericean
b0cd66509c fix for shared get max session 2014-03-20 12:53:36 +02:00
DanB
4d02ede7df Shared balance local tests improvements 2014-03-20 10:39:04 +01:00
DanB
d77c5463ac Adding local test for MaxSessionTime out of shared balance 2014-03-19 21:57:23 +01:00
Radu Ioan Fericean
19b7d0beb7 max session caclulus to consider shared groups 2014-03-19 19:09:25 +02:00
Radu Ioan Fericean
315eddb63f moved locking before debit 2014-03-19 17:32:02 +02:00
Radu Ioan Fericean
acda24e46e ansible variables 2014-03-19 15:06:46 +02:00
Radu Ioan Fericean
af16503069 added profile variables to ansible 2014-03-19 13:06:51 +02:00
Radu Ioan Fericean
822a921a37 more ansible provision commands 2014-03-18 19:41:06 +02:00
DanB
6017827059 Small tweaks to make local tests running with vagrant 2014-03-18 14:55:46 +00:00
DanB
3892d9ed20 LoadTariffPlanFromFolder, tutfscsv tests fixup 2014-03-18 14:09:09 +01:00
Radu Ioan Fericean
5322063103 use *mine_random as default strategy 2014-03-18 11:09:49 +02:00
DanB
e8393b9bcc Modified sample tariffplan for fs_json tutorial 2014-03-18 08:43:07 +01:00
Radu Ioan Fericean
724ebdc039 split rating profile and account aliases 2014-03-17 21:28:25 +02:00
DanB
2dbc80166e Adding sharedGroup loading inside csv and db loaders, tariff plans for fs_json tutorial 2014-03-17 19:10:21 +01:00
DanB
ccac8236f4 Fix test localtest for new rating IdCC 2014-03-17 17:55:46 +01:00
DanB
3c34310ab2 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-03-17 17:46:57 +01:00
DanB
2ef0f4f492 Adding regexp rules for shared groups csv file 2014-03-17 17:46:53 +01:00
Radu Ioan Fericean
f6741c6d88 Added rating id and destination id to call cost 2014-03-17 18:37:50 +02:00
DanB
62e9d02239 Fix regexp rules to accept aliases 2014-03-17 16:18:52 +01:00
DanB
2e80f8a1f4 Fixup sharedGroup number of columns 2014-03-17 15:58:33 +01:00
DanB
3aecec9bb8 Renaming cdrexporter package into cdre 2014-03-17 10:00:00 +01:00
DanB
e2e3b8d2f2 Local tests for GetCallCostLog API, LogCallCost and GetCallCostLog storage methods 2014-03-16 21:01:27 +01:00
DanB
86317e4b19 Adding apier/cdrs.go file 2014-03-16 19:55:52 +01:00
DanB
f2f46f8054 Adding GetCallCostLog API and console command 2014-03-16 19:51:30 +01:00
DanB
c66f4f2710 Local tests for SetCdr, SetRatedCdr and GetStoredCdrs 2014-03-16 18:49:05 +01:00
DanB
ee31976401 Adding detailed CDR export filters both in storage and APIs 2014-03-16 16:21:18 +01:00
DanB
f6d16cecc5 Adding full CDR template in exported CDRs, using RSRFields 2014-03-16 13:11:08 +01:00
DanB
db433a760f Adding search/replace regexp rules for FreeSWITCH extra fields 2014-03-15 19:17:40 +01:00
DanB
47700a924f Adding RSRField and tests around 2014-03-15 18:48:30 +01:00
DanB
548a4ea64f Adding ReSearchReplace struct 2014-03-15 16:30:17 +01:00
DanB
3227434fd9 Adding tests for search&replace functions 2014-03-15 14:19:04 +01:00
DanB
c344554bbb Merge branch 'master' of https://github.com/cgrates/cgrates 2014-03-14 21:14:10 +01:00
DanB
57be22dd87 Adding search and replace regexp utils 2014-03-14 21:14:05 +01:00
Radu Ioan Fericean
a3cc1fedfd use unixnano for random 2014-03-14 19:53:47 +02:00
Radu Ioan Fericean
6685b7b444 add code comment for fscdr body 2014-03-14 19:31:52 +02:00
Radu Ioan Fericean
10b27fa978 better test for random shared balance 2014-03-14 19:30:55 +02:00
Radu Ioan Fericean
e2a50a77fd search whole cdr if extra field not found in variables 2014-03-14 19:21:56 +02:00
DanB
00b47c1367 More local tests for tutorials 2014-03-13 19:50:49 +01:00
DanB
cdd83ea531 Adding local tests for tutfsjson 2014-03-13 18:40:32 +01:00
DanB
d33299f0ab Merge branch 'master' of https://github.com/cgrates/cgrates 2014-03-13 17:36:04 +01:00
DanB
5083c7479d Adding re-cache in case of LoadRatingProfile also 2014-03-13 17:35:49 +01:00
DanB
37b21d2bb5 Fixup re-cache in case of SetRatingProfile api 2014-03-13 17:01:02 +01:00
DanB
e09cc8527e Empty call costs in session should not be saved since they break 2014-03-12 19:00:21 +01:00
Radu Ioan Fericean
0855d09fea shared group strategy fully customizable 2014-03-11 18:15:12 +02:00
DanB
c8553e9611 Add 1007 as prepaid account in fs_json tutorial to test shared balances 2014-03-10 11:19:17 +01:00
DanB
d8fb33aee3 Fix cdrs port in fs_json tutorial, add 1006 as prepaid account to test shared balances 2014-03-10 11:17:45 +01:00
DanB
3f30cdb7ed Concurrency fixup on saveOperations in emulation mode 2014-03-08 18:34:22 +01:00
DanB
b9681f2d1c Adding checkConfigSanity integrated in config type 2014-03-08 17:37:02 +01:00
DanB
d6a0825142 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-03-08 16:22:43 +01:00
DanB
8d61099de1 SessionManager - Multiple sessions emulated out of one request to support scenarios like reseller chains 2014-03-08 16:22:22 +01:00
Radu Ioan Fericean
4e42c69df6 merged master 2014-03-07 12:17:45 +02:00
Radu Ioan Fericean
6ba20aee03 smal fix for csv header 2014-03-07 12:16:27 +02:00
Radu Ioan Fericean
21b80c992b started LCR work 2014-03-07 12:15:28 +02:00
Radu Ioan Fericean
56faf676dd smal fix for csv header 2014-03-07 12:12:31 +02:00
DanB
b28a02512c Adding runids in session manager config 2014-03-06 19:49:24 +01:00
DanB
5ae7a18283 Adding setupTime to CDRs for later stats calculation 2014-03-06 15:47:39 +01:00
DanB
5b10f63c94 Reload cache on SetRatingProfile API 2014-03-05 19:10:27 +01:00
DanB
90e3791c0d Merge branch 'master' of https://github.com/cgrates/cgrates 2014-03-05 18:34:35 +01:00
Radu Ioan Fericean
fdeecafe37 make sure the aliases are up-to-date after load 2014-03-05 16:54:29 +02:00
Radu Ioan Fericean
71a50dc78e duplicate check for db loader too 2014-03-05 14:56:54 +02:00
Radu Ioan Fericean
8f47264248 check for duplicate account actions 2014-03-05 14:51:18 +02:00
Radu Ioan Fericean
a753a55119 fixed connectFee in max debit issue 2014-03-05 14:17:47 +02:00
DanB
2556fb5e69 Fix in documentation changed default CDRs port which is now 2080 2014-03-05 12:06:09 +01:00
Radu Ioan Fericean
0b0474fa23 fix for MaxDebit bug
max duration was calculated badly
2014-03-04 22:02:20 +02:00
Radu Ioan Fericean
8967c2fa3a fix rounding up problem 2014-03-03 20:37:13 +02:00
DanB
bea8434d99 Adding apier_local_test.cfg file 2014-03-02 18:15:22 +01:00
DanB
bd5b2607e8 More local tests changes to write in /tmp 2014-03-02 18:09:39 +01:00
DanB
62ccb5b9df Automatic starting daemons out of sample configs in localtests, /tmp as output folder to work in non-root mode 2014-03-02 17:34:03 +01:00
DanB
755ab02d55 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-03-02 12:09:15 +01:00
DanB
cf0179e505 Adding FS_SIP_REQUSER as fallback variable for destination in case of fscdr 2014-03-02 12:09:09 +01:00
Radu Ioan Fericean
bde488c46b ignore vagrant ansible host file 2014-03-02 12:56:14 +02:00
Radu Ioan Fericean
68aefe4912 removed vagrant generated file 2014-03-02 12:56:14 +02:00
DanB
487a1c5da6 Fix tariffplan data and sleep in tutfscsv_local_test 2014-03-02 09:43:27 +01:00
DanB
64711c6b87 Sleep in local tests to allow topup to happen 2014-03-02 09:29:41 +01:00
DanB
781db890d1 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-03-01 10:29:23 +01:00
DanB
7b618f8806 Preparing packaging for rc4 2014-03-01 10:29:16 +01:00
Radu Ioan Fericean
9006c5ed18 removed logs from tests 2014-03-01 00:11:05 +02:00
Radu Ioan Fericean
e6b02b84c0 extra tests for topup after load 2014-03-01 00:02:56 +02:00
Radu Ioan Fericean
b557e2a5a4 fixed connect fee issues 2014-02-28 23:24:55 +02:00
Radu Ioan Fericean
9a06d5e537 fixes for ansible devel 2014-02-28 18:40:39 +02:00
Radu Ioan Fericean
be8ea3bcc9 fixed devel ansible 2014-02-28 18:40:39 +02:00
Radu Ioan Fericean
4833eb94c3 splitted in devel/release 2014-02-28 18:40:39 +02:00
Radu Ioan Fericean
f717bb9542 changed travis rules and removed stats 2014-02-28 18:40:39 +02:00
DanB
2646e8fa6e Local tests to capture toup 2014-02-28 10:46:28 +01:00
DanB
ed313c3222 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-02-28 10:46:02 +01:00
Radu Ioan Fericean
25b15b8f0f map storage has compression too
only needed for better emulating redis
2014-02-28 11:08:03 +02:00
Radu Ioan Fericean
7d9326965d don't overwrite account on load 2014-02-28 10:47:30 +02:00
DanB
52ae44fc34 Fixup sample tariffplans 2014-02-27 17:48:45 +01:00
DanB
a0aa0cc05d Merge branch 'master' of https://github.com/cgrates/cgrates 2014-02-27 17:38:49 +01:00
DanB
b9d52c6401 Fix validation for Actions, now with shared balances 2014-02-27 17:38:42 +01:00
Radu Ioan Fericean
48d1720430 refund credit to shared group accounts 2014-02-27 17:12:59 +02:00
Radu Ioan Fericean
39e93e7fb8 fixses and tests for shared balances 2014-02-27 16:54:11 +02:00
Radu Ioan Fericean
49a3df285b test for shared group members loading 2014-02-27 15:30:29 +02:00
Radu Ioan Fericean
fc91e35433 shared balance fix 2014-02-27 14:04:59 +02:00
Radu Ioan Fericean
95d4af8ab7 more rounding and maxxsession improvement 2014-02-27 12:23:11 +02:00
Radu Ioan Fericean
83dd4efeab more aliases tests 2014-02-27 09:34:48 +02:00
Radu Ioan Fericean
b3dee97bc4 fixses and tests for aliases 2014-02-26 20:17:59 +02:00
Radu Ioan Fericean
074313b0f8 first aliases implementation
tests pending
2014-02-26 18:29:49 +02:00
Radu Ioan Fericean
8d98436656 rounding after debit 2014-02-26 11:14:13 +02:00
Radu Ioan Fericean
477af9467f added load test for shared groups 2014-02-26 11:14:13 +02:00
DanB
1a8e7f4631 Testing tutfscsv including maxDebit and unitsCounter for general usage 2014-02-25 20:52:11 +01:00
DanB
670b748b01 Renaming tag->id and old id->tbid in mysql tables 2014-02-25 13:53:13 +01:00
DanB
ddbd97ad20 Fix merge in tutfscsv_local 2014-02-24 12:47:23 +01:00
DanB
fe4506008f Local test TutFsCsv from config file 2014-02-24 12:41:24 +01:00
Radu Ioan Fericean
0aa5223f78 Merge branch 'master' into shared_balances
Conflicts:
	engine/account.go
	engine/loader_csv_test.go
	engine/storage_interface.go
	engine/storage_map.go
	engine/storage_redis.go
2014-02-21 16:36:48 +02:00
Radu Ioan Fericean
a37dbf0734 renamed Account Type attribute
It's now AllowNegative and it's a boolean (was *prepaid/*postpaid)
2014-02-21 15:52:47 +02:00
Radu Ioan Fericean
7831a53797 renamed USerBalance to Account 2014-02-21 14:37:31 +02:00
Radu Ioan Fericean
f701a42948 renamed action's BalanceId to BalanceType
also added a topup test
2014-02-21 13:14:06 +02:00
Radu Ioan Fericean
164b9f8945 fixed database credentials 2014-02-20 23:12:53 +02:00
Radu Ioan Fericean
a3daecdbc9 first running vagrant-ansible version 2014-02-20 22:13:43 +02:00
Radu Ioan Fericean
a91873800d started ansible vagrant box 2014-02-20 22:00:05 +02:00
Radu Ioan Fericean
f463dd2755 added *daily and *yearly for balance expiration time 2014-02-20 18:07:10 +02:00
Radu Ioan Fericean
f052b14214 added some comments 2014-02-20 17:29:13 +02:00
Radu Ioan Fericean
5f1923d694 Merge branch 'master' into shared_balances 2014-02-18 15:38:03 +02:00
Radu Ioan Fericean
c52b161b73 allow seetting rate subject and expiration with AddBalance api 2014-02-18 15:37:03 +02:00
DanB
d7a0446585 Fixup localtests 2014-02-18 11:06:09 +01:00
DanB
216ae2b767 Fixup install documentation to reflect new repo path 2014-02-18 09:27:28 +01:00
DanB
e60fc8cb96 Capturing the output of the engine in tests 2014-02-18 09:25:10 +01:00
Radu Ioan Fericean
8008c76154 Merge branch 'master' into shared_balances 2014-02-17 18:05:54 +02:00
DanB
d929e14159 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-02-17 16:25:41 +01:00
DanB
dd2eb2f97a session_manager/event interface with field filter, preparing for multiple sessions 2014-02-17 16:25:22 +01:00
Radu Ioan Fericean
e0476a3130 Add members on balance creation 2014-02-17 14:50:35 +02:00
Radu Ioan Fericean
fdc451244a fixed typo 2014-02-17 01:28:50 +02:00
Radu Ioan Fericean
098a1f6d3f fixed account actions ids 2014-02-17 01:22:55 +02:00
Radu Ioan Fericean
75046b1f36 more shared group fixes 2014-02-17 01:16:31 +02:00
Radu Ioan Fericean
dec4ebf2f1 added distinct field parameter 2014-02-17 01:09:37 +02:00
Radu Ioan Fericean
4154a3beef Merge ssh://192.168.0.9/home/rif/Documents/prog/go/src/github.com/cgrates/cgrates into shared_balances 2014-02-17 00:43:04 +02:00
Radu Ioan Fericean
0efb2f1094 load shared groups from csv 2014-02-17 00:42:41 +02:00
Radu Ioan Fericean
5371688158 Merge ssh://192.168.0.9/home/rif/Documents/prog/go/src/github.com/cgrates/cgrates into shared_balances 2014-02-17 00:40:57 +02:00
Radu Ioan Fericean
51ee4091bc shared group fixes 2014-02-17 00:39:15 +02:00
Radu Ioan Fericean
540412033f used local addres in tests 2014-02-16 23:13:27 +02:00
Radu Ioan Fericean
a731a33238 Merge branch 'master' into shared_balances 2014-02-16 22:52:20 +02:00
Radu Ioan Fericean
96092faffc Moved build status up 2014-02-13 13:50:41 +02:00
Radu Ioan Fericean
beea40977c Fixed bad copy paste 2014-02-13 13:24:31 +02:00
Radu Ioan Fericean
ffaeac112c Update README.md 2014-02-13 13:23:27 +02:00
Radu Ioan Fericean
9c46b61c73 Api links point to apier package 2014-02-13 13:17:00 +02:00
Radu Ioan Fericean
1ab852e3cd Update README.md 2014-02-13 13:14:51 +02:00
Radu Ioan Fericean
4e956eaa8c Update README.md 2014-02-13 13:14:38 +02:00
DanB
a5f5274095 Adding config for additional sessions management 2014-02-13 11:55:10 +01:00
DanB
4c894ee9de Adding original apier_local_test 2014-02-13 10:13:12 +01:00
Radu Ioan Fericean
361d75d8cf compressed all GetTP*Ids methods into one 2014-02-13 00:01:58 +02:00
Radu Ioan Fericean
64e79764de build fix 2014-02-12 16:36:56 +02:00
Radu Ioan Fericean
6ad5794bfc added db loader for shared balances 2014-02-12 16:23:03 +02:00
Radu Ioan Fericean
c447fa49dc Merge branch 'master' into shared_balances 2014-02-12 14:18:03 +02:00
DanB
9042c98492 Fixup mediation with rerating 2014-02-11 12:57:00 +01:00
DanB
bd71dc9a4c Test fixups 2014-02-10 20:00:30 +01:00
DanB
944262ccff Adding local_test for mediator rpc method 2014-02-10 18:31:27 +01:00
Radu Ioan Fericean
fc1e7aed5a merged master 2014-02-10 15:12:01 +02:00
DanB
b91fc4ca21 Adding mediator_rpc 2014-02-10 14:08:24 +01:00
Radu Ioan Fericean
b784edf5aa Merge branch 'master' into shared_balances
Conflicts:
	engine/balances.go
	utils/consts.go
2014-02-07 22:32:03 +02:00
Radu Ioan Fericean
914fe77117 rating subject can have multiple predefined values
Valid values can start with *zero + any duration 1m, 15m, 1h10m
2014-02-07 22:26:39 +02:00
Radu Ioan Fericean
77ea2754cb GetMaxSessionDuration fix
should return no more than initial call descriptor duration
2014-02-07 19:54:09 +02:00
Radu Ioan Fericean
cd531849d1 Merge branch 'master' into shared_balances 2014-02-07 19:12:28 +02:00
Radu Ioan Fericean
dadf06f3ef get max session duration method was modifing call descriptor 2014-02-07 19:10:44 +02:00
Radu Ioan Fericean
9e88719e05 fixed build problem 2014-02-07 12:24:52 +02:00
Radu Ioan Fericean
3a3b92e9d6 little cleanup 2014-02-07 11:13:39 +02:00
Radu Ioan Fericean
a9d698e029 Merge branch 'master' into shared_balances
Conflicts:
	engine/balances.go
	engine/userbalance.go
2014-02-07 10:57:29 +02:00
Radu Ioan Fericean
aa99a1e526 make sure connect fee is deducted only on LoopIndex = 0 2014-02-06 19:36:42 +02:00
Radu Ioan Fericean
f154dac933 connect fee changes
The connect fee is take from the first timespan after balance debit.
So if the balance has ratingsubject than that connect fee will be applied.
2014-02-06 18:56:21 +02:00
Radu Ioan Fericean
59f650e3f8 small addition last night fix 2014-02-06 10:15:27 +02:00
Radu Ioan Fericean
462152a76d fixed rating with rating subject bug 2014-02-06 01:34:38 +02:00
Radu Ioan Fericean
3bf3f04cc6 removed connect fee as a separte field in call cost 2014-02-06 01:34:38 +02:00
DanB
9f9174d0cc Console get_maxduration command implementation 2014-02-05 18:51:15 +01:00
DanB
e6ef9f8155 DebitBalance console command, fix apier local_test for GetUserBalances 2014-02-05 17:59:49 +01:00
Radu Ioan Fericean
bea20cf98d fixed history files creation 2014-02-05 14:08:49 +02:00
Radu Ioan Fericean
59e6e945b5 small typo fix 2014-02-05 14:08:37 +02:00
Radu Ioan Fericean
77c326cccf action rating subject may or may not start with a * 2014-02-04 20:54:48 +02:00
Radu Ioan Fericean
709594ffa9 get_balance console renamed to get_balances
returns json formatted userbalance object
2014-02-04 15:28:02 +02:00
Radu Ioan Fericean
58d485f46f small console fixes 2014-02-04 14:40:58 +02:00
DanB
4cbadb4158 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-02-03 19:04:53 +01:00
DanB
caba3af732 Mediation RPC with rerate abilitites, rename RatedCDR -> StoredCdr, adding storage interface tests 2014-02-03 19:04:42 +01:00
Radu Ioan Fericean
727337e617 start shared groups cvs loading
modified shared group structure
2014-02-03 18:50:26 +02:00
Radu Ioan Fericean
20a0ae4449 removed direction and added destinationid to add_balance console command
th consoled is aimed to be used for simple and quick test actions
2014-02-03 17:34:37 +02:00
Radu Ioan Fericean
5f331f3161 add direction parameter for add_balance console command 2014-02-03 15:13:22 +02:00
Radu Ioan Fericean
c7ea2cd263 miminmum match length is now parametrized and can be moved in the confs
Note that it affects the general speed
2014-02-03 10:53:32 +02:00
Radu Ioan Fericean
30a842a6b9 miminmum match length is now parametrized and can be moved in the confs
Note that it affects the general speed
2014-02-01 14:04:12 +02:00
DanB
3f7bc14b44 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-01-31 19:31:15 +01:00
Radu Ioan Fericean
6fcd794005 moved hist init before populate 2014-01-31 18:49:49 +02:00
Radu Ioan Fericean
750260505c build fix 2014-01-31 17:22:32 +02:00
Radu Ioan Fericean
5c2d1a9c1a Merge branch 'master' into shared_balances 2014-01-31 17:08:44 +02:00
Radu Ioan Fericean
d90e67b966 moved get next start date from shared balances branch 2014-01-31 17:07:36 +02:00
Radu Ioan Fericean
0b7f315d49 match one symbol prefix as well 2014-01-31 17:05:05 +02:00
Radu Ioan Fericean
a239b8d760 bigger refactoring for next start date calculus 2014-01-31 16:37:48 +02:00
Radu Ioan Fericean
0490eaef8c split shared debiting methods minutes/money 2014-01-29 21:54:17 +02:00
Radu Ioan Fericean
ea6c8371d2 Merge branch 'master' into shared_balances 2014-01-29 21:45:05 +02:00
Radu Ioan Fericean
4032274748 fixed next start time test 2014-01-29 18:39:36 +02:00
DanB
edf5007f9e Session manager following api changes in FSock 2014-01-29 11:02:37 +01:00
Radu Ioan Fericean
31eebb7e81 Merge branch 'master' into shared_balances 2014-01-28 13:00:40 +02:00
Radu Ioan Fericean
5d190f0f2a add actions for enabling/disabling user 2014-01-28 13:00:04 +02:00
Radu Ioan Fericean
a5fad89574 added disabling facility for user accounts 2014-01-28 12:22:50 +02:00
Radu Ioan Fericean
23a676da95 got engine from master 2014-01-27 11:46:59 +02:00
Radu Ioan Fericean
69cf1cc896 Merge branch 'master' into shared_balances
Conflicts:
	apier/v1/apier_local_test.go
	apier/v1/tutfscsv_local_test.go
	cmd/cgr-engine/cgr-engine.go
	engine/storage_map.go
	engine/storage_mongo.go
	engine/storage_redis.go
2014-01-27 11:18:25 +02:00
Radu Ioan Fericean
b735688fe5 Remaned to hasdata method 2014-01-27 11:04:25 +02:00
DanB
6a251c2b2c Tutorials config updates for rc4 2014-01-26 10:33:02 +01:00
DanB
21b4c10836 Tests - cdrc/RecordAsRatedCdr ratedcdr/AsRawCdrHttpForm 2014-01-26 09:45:51 +01:00
DanB
1bbe5a0a88 CDRc communicating over internal interface with the CDRs 2014-01-25 20:52:32 +01:00
DanB
8733b5219e Mediator and SessionManager wait for cache to come up before starting over internal interface 2014-01-25 14:28:03 +01:00
DanB
f61aca8d91 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-01-25 14:03:18 +01:00
Radu Ioan Fericean
3bbb67c72f working variant for history server and gob enc 2014-01-25 11:52:11 +02:00
Radu Ioan Fericean
8177e64f8b new histtory tests 2014-01-25 11:52:11 +02:00
DanB
9210b20924 Fmt on sources :( 2014-01-25 10:49:00 +01:00
DanB
5b424d0e70 Engine components sync via chans 2014-01-25 10:46:21 +01:00
Radu Ioan Fericean
b6dde967f2 samll engine fix 2014-01-24 17:35:57 +02:00
Radu Ioan Fericean
c50c202fe6 Merge branch 'master' into shared_balances
Conflicts:
	apier/v1/apier_local_test.go
	apier/v1/tutfscsv_local_test.go
	cmd/cgr-engine/cgr-engine.go
2014-01-24 10:26:55 +02:00
Radu Ioan Fericean
5f9d18fe0f removed extra log 2014-01-24 09:37:30 +02:00
Radu Ioan Fericean
a31231de09 merge 2014-01-24 09:33:32 +02:00
DanB
ddeb13bcc4 Engine start order, improved logging, default config listening fix 2014-01-23 18:19:03 +01:00
Radu Ioan Fericean
70d0e0171c better historyy agent starting 2014-01-23 18:22:50 +02:00
DanB
ba3cabb7bb Extra log in engine for history 2014-01-23 17:03:43 +01:00
DanB
5d857f1255 Fix local tests 2014-01-23 13:18:00 +01:00
DanB
4f3e91d8ca Default client-server over internal connections, small fixup order of service start 2014-01-23 12:45:13 +01:00
DanB
7c52e1e692 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-01-23 11:05:06 +01:00
Radu Ioan Fericean
81cc68e0c5 removed exit after cdrs 2014-01-23 11:59:05 +02:00
DanB
f51e0b10e3 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-01-23 10:51:03 +01:00
Radu Ioan Fericean
999ac0aead added server log messages 2014-01-23 11:44:14 +02:00
DanB
02297d4d36 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-01-23 10:29:41 +01:00
DanB
95fde288ad Fixup mailer 2014-01-23 10:29:31 +01:00
Radu Ioan Fericean
ea75cd3aa2 cgr-engine main is async again 2014-01-23 11:25:32 +02:00
Radu Ioan Fericean
21875f7ed7 compiation fixes 2014-01-23 11:13:29 +02:00
Radu Ioan Fericean
7697b74713 Merge branch 'master' into shared_balances
Conflicts:
	apier/v1/apier.go
	cmd/cgr-engine/cgr-engine.go
	engine/storage_redis.go
2014-01-23 10:26:29 +02:00
DanB
46b87953da Fix test default config mailer 2014-01-22 23:28:05 +01:00
DanB
5e72e1c528 Adding mail_async action 2014-01-22 22:14:06 +01:00
DanB
baf590dab3 Renaming ActionTimings -> ActionPlans inside tutorial samples 2014-01-22 21:09:20 +01:00
DanB
2c3e8e1584 Adding mailer configuration 2014-01-22 21:06:57 +01:00
Radu Ioan Fericean
962cf17884 updated config file 2014-01-22 21:02:23 +02:00
Radu Ioan Fericean
ac60a39852 removed the async stuff from main and fixed method Lock has wrong number
of ins: 1 issue
2014-01-22 20:55:37 +02:00
Radu Ioan Fericean
0005ba2f68 RPC server changes
rpc server now serves both json and gob, refactored server code into
it's own type
2014-01-22 19:53:30 +02:00
DanB
08e5d8540a callUrlAsync using previous json encoded balance for error logging 2014-01-21 17:52:03 +01:00
DanB
6c96937059 Async call url logs failures 2014-01-21 13:31:53 +01:00
DanB
080395e7ea Adding async call_url action 2014-01-21 13:03:47 +01:00
DanB
2896053199 Renaming ActionsTimings into ActionPlan so we avoid in the future redis store changes as much as possible 2014-01-19 20:14:46 +01:00
DanB
0348be416c ActionTimings -> ActionPlan in APIs, storage tables and redis key 2014-01-19 20:02:32 +01:00
DanB
13707ca377 Increased cgrates version for next packaging 2014-01-18 19:47:00 +01:00
DanB
8ec403f151 Fixup reloading cache keys on csv/db load, removed automatic caching in db/set methods since it makes no sense/trouble in distributed environments 2014-01-18 19:45:23 +01:00
DanB
abd0a00ecb Fix packaging to uncompress freeswitch config 2014-01-18 09:12:59 +01:00
DanB
e97b72a30a Archiving FreeSWITCH tutorial configs since tools like ohloh.net are counting them as CGRateS sources 2014-01-18 08:53:52 +01:00
DanB
a3048a696f Merge branch 'master' of https://github.com/cgrates/cgrates 2014-01-15 18:57:44 +01:00
DanB
4065bcb58f Fixup rates tutorial json 2014-01-15 18:57:17 +01:00
Radu Ioan Fericean
3c622d317a warning when duplicating timings 2014-01-15 17:45:39 +02:00
DanB
9f84950287 Localtest for tutfscsv, Monit config for cgrates 2014-01-15 15:24:09 +01:00
DanB
6c82f01a10 Tutorial rates fixup, apier reload cache after loading from file, init script creates rundir 2014-01-15 15:23:32 +01:00
DanB
d8f2fc8521 Fixup config tutorial 2014-01-14 22:34:33 +01:00
DanB
9caed50e0a Adding FS_JSON tutorial configs, fixup action log to dump balance as json, improvments csv tutorial 2014-01-14 21:15:06 +01:00
Radu Ioan Fericean
ea6337d9fa updated engine command 2014-01-14 19:23:05 +02:00
Radu Ioan Fericean
2a1f6ff583 Merge branch 'master' into shared_balances 2014-01-14 19:17:11 +02:00
Radu Ioan Fericean
657100c1e8 I did not run test locally, my bad 2014-01-14 19:16:28 +02:00
Radu Ioan Fericean
b7cdf53858 Merge branch 'master' into shared_balances 2014-01-14 19:11:33 +02:00
Radu Ioan Fericean
4738f7d882 fix for storage interface nil 2014-01-14 19:10:47 +02:00
DanB
2c1b96c6b3 Console AddBalance with Weight 2014-01-14 17:23:53 +01:00
Radu Ioan Fericean
99e269b894 Merge branch 'master' into shared_balances 2014-01-14 18:20:29 +02:00
DanB
187d9476f6 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-01-14 16:38:03 +01:00
Radu Ioan Fericean
4b0ccb7564 protection against recursive action execution from triggers 2014-01-14 17:26:16 +02:00
DanB
a0d76f8dda Fixup in apier_local_test 2014-01-14 13:36:38 +01:00
DanB
765ab1c600 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-01-14 12:26:14 +01:00
DanB
e6ffd56479 Console adding Overwrite 2014-01-14 12:26:10 +01:00
Radu Ioan Fericean
d28815bf0a execute action triggers on debit action 2014-01-14 13:24:06 +02:00
DanB
77fa996a7d Fixup cgr-engine 2014-01-14 11:16:02 +01:00
DanB
c0b6562a54 Fixup tutorial init script 2014-01-14 10:37:53 +01:00
DanB
845ecc8fd9 Bug fixup in reloading cache on startup(actions ignored) 2014-01-14 10:18:41 +01:00
DanB
49ab537be8 Init script fixups 2014-01-14 10:09:22 +01:00
Radu Ioan Fericean
c50a61fec5 vet fixing 2014-01-13 22:37:10 +02:00
Radu Ioan Fericean
d765e0090b Merge branch 'master' into shared_balances
Conflicts:
	engine/loader_csv.go
2014-01-13 22:34:19 +02:00
Radu Ioan Fericean
d776219013 added user info to balance for shared balances finding 2014-01-13 22:11:51 +02:00
DanB
9feadce362 Fixups freeswitch rotate script, tutorial config 2014-01-13 20:57:18 +01:00
DanB
9428ffa9c6 Fixups for loader to work with dry_run when destinations file not provided, config fixups, doc fixups 2014-01-13 20:24:36 +01:00
Radu Ioan Fericean
7bc182e374 various go vet fixes 2014-01-13 17:30:06 +02:00
DanB
993c5400aa Static fields in CDRC tutorial example, rotate csv files by script provided 2014-01-13 13:47:25 +01:00
DanB
758f399acb Adding static fields inside cdrc config 2014-01-13 13:15:16 +01:00
DanB
91bae964ba Fixup CDRC to only log on errors but not stop the server, adding freeswitch rotate cdrs script 2014-01-12 19:56:27 +01:00
DanB
43c097aa98 Fixup config 2014-01-12 19:22:18 +01:00
DanB
30681fa7d4 FS-CSV Tutorial updates 2014-01-12 19:15:00 +01:00
DanB
c6bbd32298 Adding pseudoprepaid in session manager un-park 2014-01-12 18:54:04 +01:00
DanB
157ef5359b Merge branch 'master' of https://github.com/cgrates/cgrates 2014-01-12 18:37:40 +01:00
DanB
8a323544e8 Adding SMCallMaxDuration setting 2014-01-12 18:37:18 +01:00
Radu Ioan Fericean
f6758d0d0f More fixes for debit loop call duration 2014-01-12 19:34:56 +02:00
Radu Ioan Fericean
fb1c7265f5 Merge branch 'master' into shared_balances 2014-01-12 19:09:12 +02:00
Radu Ioan Fericean
995cfd9785 fixed debit loop callend issue 2014-01-12 19:08:47 +02:00
DanB
d56bec9338 More fixups in the fs_tutorials 2014-01-12 11:02:54 +01:00
DanB
d2ffa364a2 Copyright/version updated, doc links fixups 2014-01-12 10:58:39 +01:00
DanB
47236109b2 Splitting FS tutorials is multiple pages 2014-01-12 10:52:49 +01:00
DanB
7f9681513b Tutorial FS_CSV: adding info 2014-01-12 10:30:28 +01:00
DanB
34f3dc9e0f Tutorial documentation, fs_csv tariffplans refactoring 2014-01-11 20:27:04 +01:00
DanB
0924f4104c Fixup loader to take from datadir, adding tutorials/fs_csv/cgrates configurations 2014-01-11 16:41:06 +01:00
DanB
e4b5c98367 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-01-10 20:50:30 +01:00
DanB
ae5118dc0b Fixup data path in local tests 2014-01-10 20:49:58 +01:00
DanB
77c7f205b0 Adding fs_csv freeswitch configs for tutorial 2014-01-10 20:39:25 +01:00
Radu Ioan Fericean
ca6957cb48 Merge branch 'master' into shared_balances
Conflicts:
	engine/accountlock.go
2014-01-10 19:04:55 +02:00
Radu Ioan Fericean
8aafd0ec77 better guardmany locking 2014-01-10 19:02:40 +02:00
DanB
9f125e638f Apier: AddAccount -> SetAccount 2014-01-10 12:51:37 +01:00
Radu Ioan Fericean
f393b600d4 better locking for account guard 2014-01-10 11:46:29 +02:00
DanB
2cd548b810 Adding ActionTimings lock in scheduler 2014-01-10 10:44:09 +01:00
DanB
29edca794b Merge branch 'master' of https://github.com/cgrates/cgrates 2014-01-10 09:10:10 +01:00
Radu Ioan Fericean
127e7650fa moved locking down south 2014-01-09 22:31:08 +02:00
DanB
66190b615a Merge branch 'master' of https://github.com/cgrates/cgrates 2014-01-09 21:18:09 +01:00
Radu Ioan Fericean
0278f08de4 more lock releasing 2014-01-09 22:16:49 +02:00
DanB
84d16bcce5 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-01-09 21:00:30 +01:00
Radu Ioan Fericean
7ee915e916 avoid keeping mutex locked over scheduler wait 2014-01-09 21:59:41 +02:00
DanB
3208a78965 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-01-09 20:03:11 +01:00
DanB
98fc855ee0 Overwrite parameter added on AddBalance 2014-01-09 20:03:01 +01:00
Radu Ioan Fericean
1471c47bda started adding test for shared group and created utility methods for
getting shared balances
2014-01-09 20:06:32 +02:00
Radu Ioan Fericean
c8fb090c11 meged master branch 2014-01-09 17:21:27 +02:00
Radu Ioan Fericean
23a95ed890 added mutex for schedular loop 2014-01-09 17:11:34 +02:00
Radu Ioan Fericean
1c2d2116fd little improvement 2014-01-09 16:57:44 +02:00
DanB
12e896576c Adding Apier tests for cdrserver, newly added commands for account triggers and account timings 2014-01-09 13:32:52 +01:00
DanB
79497e8970 APIs - locking balance and action triggers when operating on them, adding reloadScheduler attribute in some methods 2014-01-09 10:44:38 +01:00
DanB
07dd19a9d5 Adding Get and Rem APIs for AccountTriggers 2014-01-08 21:30:15 +01:00
Radu Ioan Fericean
3f16c1c12d debit minutes logic 2014-01-08 22:15:11 +02:00
DanB
3f506b50c8 Fixup Get and Rem ActionTiming API methods 2014-01-08 20:36:29 +01:00
DanB
b7d9da842b Adding AccountActionTiming Get and Remove APIs 2014-01-08 14:00:20 +01:00
Radu Ioan Fericean
9854ce7068 fixed build 2014-01-07 22:38:38 +02:00
Radu Ioan Fericean
58ddae9b70 Started shared balances logic 2014-01-07 22:34:40 +02:00
DanB
afe845a8aa Enhancing cdrexporter with dry_run, console export_cdrs command 2014-01-07 20:27:45 +01:00
DanB
99a9b2c987 Fixup cdrc to support freeswitch_csv type (ignore .files ending with .csv) 2014-01-06 12:09:51 +01:00
DanB
caa2ed7776 Config to default tor, tenant and subject towards more logical fields 2014-01-06 10:46:29 +01:00
DanB
1a53359c5f Formatting doc default configuration 2014-01-05 19:53:43 +01:00
DanB
b8075c2835 Adding updated default configuration inside documentation 2014-01-05 19:45:09 +01:00
DanB
2a23a29b25 Further formatting on postinstall doc 2014-01-05 19:39:26 +01:00
DanB
4481b170eb Fixup git link 2014-01-05 19:34:18 +01:00
DanB
b122a2a748 Doc formating 2014-01-05 19:32:03 +01:00
DanB
4e4d411a6a Installation doc updated for .rc3 2014-01-05 19:29:06 +01:00
DanB
6b2e0786d2 Adding db setup script 2014-01-05 18:23:07 +01:00
DanB
fdeab3cb09 Merge branch 'master' of https://github.com/cgrates/cgrates 2014-01-05 15:52:44 +01:00
DanB
7099b40609 Debian packaging refactored, ready for nightly packages 2014-01-05 15:42:55 +01:00
Radu Ioan Fericean
3bc4823e1d handled term signals 2014-01-03 20:34:46 +02:00
Radu Ioan Fericean
fa2ded95bc changed pid argument o string (path to file) 2014-01-03 14:00:02 +02:00
Radu Ioan Fericean
eb3bed151d write pid file from cgr-engine 2014-01-02 22:37:58 +02:00
Radu Ioan Fericean
72a415b924 action timing next date fixes 2014-01-01 19:33:18 +02:00
DanB
b8c83e7752 Fixup postinst script 2014-01-01 17:47:24 +01:00
DanB
9f45635de0 CdrE config, documentation fixup 2013-12-31 11:57:19 +01:00
DanB
4092baea56 Adding cgr-tester tool 2013-12-29 17:20:11 +01:00
DanB
5024944a2b History basic documentation, FsCgr tests fixup for localtime compliance, SaveInterval config on history server parsed as time.Duration 2013-12-29 14:17:38 +01:00
DanB
ec5417d6e4 Adding CDRC brief documentation 2013-12-29 11:14:44 +01:00
DanB
2b19fcd408 Finishing fsCdr.AsRatedCdr implementation with tests 2013-12-28 15:58:19 +01:00
DanB
8064491481 Adding share config methods and tests 2013-12-28 10:34:06 +01:00
DanB
9d5ed81791 Console command: get_cache_age 2013-12-27 20:05:11 +01:00
DanB
4eee6ddd0e Adding static field values for multiple mediation in configuration, fmt on sources 2013-12-27 20:02:47 +01:00
DanB
7f8f981085 Small fixups 2013-12-27 11:48:17 +01:00
DanB
b3a3c1f599 Adding runid in logdb 2013-12-27 10:26:02 +01:00
DanB
756affe31a Cache stats console command 2013-12-26 21:22:11 +01:00
DanB
1db3c550b1 Multiple mediation on same RawCDR, renamed CDR interface into RawCDR 2013-12-26 21:21:29 +01:00
DanB
e76b94c285 Adding get_cache_stats console command 2013-12-23 19:31:44 +01:00
DanB
75d24d0fd1 Adding GetCacheStats and GetCachedItemAge Apier methods 2013-12-23 19:23:10 +01:00
DanB
e19b8bc4cf Adding SetRatingProfile method, renaming old SetRatingProfile into LoadRatingProfile and SetRatingPlan into LoadRatingPlan 2013-12-23 16:54:21 +01:00
DanB
c04bc753fd Adding SetActions and SetActionTimings Apier methods 2013-12-23 14:26:33 +01:00
DanB
d60a5f1b79 RatedCDR maintaining CDR interface, Cdrc csv reader does not longer stops in case of row error, further test related fixups 2013-12-22 19:44:45 +01:00
DanB
1950551833 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-12-22 18:35:30 +01:00
DanB
8e95225b99 Completing the implementation of the CDR client with tests 2013-12-22 18:35:16 +01:00
DanB
c38179c01a Cdrc further refactoring 2013-12-22 13:06:40 +01:00
DanB
ca3b13651f Adding cdrc together with config dependencies and tests 2013-12-20 22:57:45 +01:00
Radu Ioan Fericean
08be8d1bb5 cutting extra decimals 2013-12-20 14:11:47 +02:00
DanB
d8977fc504 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-12-19 20:01:31 +01:00
DanB
2ba458524e CDRS with datetime instead of timestamp, adding run_ids in mediator config section 2013-12-19 20:01:19 +01:00
Radu Ioan Fericean
a74a201735 seting increment cost to 0 for increments payed using free minutes 2013-12-19 18:44:58 +02:00
Radu Ioan Fericean
6fb2ca37c2 better rates rounding 2013-12-19 18:30:48 +02:00
DanB
74f027189a Merge branch 'master' of https://github.com/cgrates/cgrates 2013-12-19 12:01:30 +01:00
DanB
990000f246 Fixup default config for different subsystems databases 2013-12-19 12:01:24 +01:00
Radu Ioan Fericean
4330e9dd59 round increment cost 2013-12-19 12:56:55 +02:00
DanB
3598b7b505 Fixup spanstress tool 2013-12-18 20:32:05 +01:00
DanB
17af9c312a Merge branch 'master' of https://github.com/cgrates/cgrates 2013-12-18 20:05:58 +01:00
DanB
f3d0c49803 Finishing implementation of different database handles for rating vs accounting subsystems 2013-12-18 20:02:47 +01:00
Radu Ioan Fericean
f7d7a34c17 typo 2013-12-18 15:38:04 +02:00
Radu Ioan Fericean
6d543f5d86 db loader stats 2013-12-18 15:37:19 +02:00
Radu Ioan Fericean
18273a920e accounting stats 2013-12-18 15:31:20 +02:00
Radu Ioan Fericean
f136f1e67d more statistics 2013-12-18 15:27:46 +02:00
Radu Ioan Fericean
4638bad4d1 replaced storageGetter with dataStorage 2013-12-17 22:37:51 +02:00
Radu Ioan Fericean
e03bbc9b96 started accounting storage 2013-12-17 21:19:38 +02:00
Radu Ioan Fericean
522ef1cf9c commented out the test taht sometimes fails on drone.io 2013-12-17 19:57:44 +02:00
Radu Ioan Fericean
cd9eaecb83 changed the way the destinations are cached 2013-12-17 19:33:04 +02:00
Radu Ioan Fericean
806570cbaf added back set caching 2013-12-17 18:14:32 +02:00
Radu Ioan Fericean
c0d7cf0001 small fixes 2013-12-17 18:02:54 +02:00
Radu Ioan Fericean
d69378a626 started new destination caching 2013-12-17 17:51:38 +02:00
Radu Ioan Fericean
3e4067b4b0 started statistics 2013-12-17 16:42:16 +02:00
Radu Ioan Fericean
dbe8bde7bf optimized prefixes container 2013-12-16 19:09:56 +02:00
Radu Ioan Fericean
d0284bf5e1 standard span stress 2013-12-16 15:27:57 +02:00
Radu Ioan Fericean
41dd86ca9a more optimizations for large destination keys 2013-12-16 15:17:20 +02:00
Radu Ioan Fericean
609f6271df added method for finding cache key age 2013-12-15 20:43:46 +02:00
Radu Ioan Fericean
aa2360f756 started reademe for stress testing tools 2013-12-14 17:08:58 +02:00
Radu Ioan Fericean
243ebdb7fa fixed compile error 2013-12-14 16:58:35 +02:00
Radu Ioan Fericean
022c426a20 further caching and optimizations 2013-12-14 16:51:28 +02:00
Radu Ioan Fericean
5a929dfeb0 some memory profiling and optimization 2013-12-14 15:20:58 +02:00
Radu Ioan Fericean
237508e590 performance enhancements 2013-12-14 12:44:42 +02:00
Radu Ioan Fericean
59543849d4 updated all stress test code 2013-12-13 22:56:16 +02:00
Radu Ioan Fericean
8a36ac72b9 improved refund testing and prepared stres test for profiling 2013-12-13 22:43:46 +02:00
Radu Ioan Fericean
fac3fa5025 renamed test_local to local_test 2013-12-13 17:27:38 +02:00
Radu Ioan Fericean
950a357127 more fixes and test for credit refounding 2013-12-13 17:26:14 +02:00
Radu Ioan Fericean
796caa1c7f fixes for refunded credit 2013-12-13 16:31:11 +02:00
Radu Ioan Fericean
a57686990c another precache fix 2013-12-13 16:21:55 +02:00
Radu Ioan Fericean
d99c0c076c fixed max debit function 2013-12-13 12:56:33 +02:00
Radu Ioan Fericean
b6e5ea82e0 substract connect fee for get max session time 2013-12-12 22:24:08 +02:00
Radu Ioan Fericean
895bf6b6c1 fixed a max session bug 2013-12-12 19:36:19 +02:00
Radu Ioan Fericean
da2ac4ea28 added some logging for max session time 2013-12-12 19:12:50 +02:00
Radu Ioan Fericean
c68660367b improved max session duration (used to check initial connect) 2013-12-12 15:45:49 +02:00
Radu Ioan Fericean
76af5ddf17 one more debit test 2013-12-11 16:39:17 +02:00
Radu Ioan Fericean
690751e3c2 try travis go version 1.2 2013-12-11 14:50:42 +02:00
Radu Ioan Fericean
00be23afb1 Merge branch '12cleanup' 2013-12-11 14:49:33 +02:00
Radu Ioan Fericean
d5f4417005 better rate subject monet debit 2013-12-11 14:48:16 +02:00
Radu Ioan Fericean
6c30c98836 Merge branch 'debit' 2013-12-10 20:24:40 +02:00
Radu Ioan Fericean
6ff0d17d5f refactored debit credit method, now modular and better tested 2013-12-10 20:24:06 +02:00
Radu Ioan Fericean
3611494936 before rewrite 2013-12-10 20:24:06 +02:00
Radu Ioan Fericean
1c4c27b2bf refactored debit credit method, now modular and better tested 2013-12-10 20:22:28 +02:00
DanB
7809fa4e8e Merge branch 'master' of https://github.com/cgrates/cgrates 2013-12-10 17:43:38 +01:00
DanB
d95dc46e65 Some more apier local tests 2013-12-10 17:43:32 +01:00
Radu Ioan Fericean
395931aba8 fixed save dusration compilation error 2013-12-09 12:59:32 +02:00
Radu Ioan Fericean
b122090421 added save period for history server 2013-12-09 12:49:56 +02:00
Radu Ioan Fericean
6f9a3ab4c0 before rewrite 2013-12-09 12:25:43 +02:00
DanB
71ca9d8fe3 Adding apier.GetRatingPlans and various local tests 2013-12-06 20:49:00 +01:00
Radu Ioan Fericean
3257010f32 added cache entries count and added prefix for cache keys 2013-12-05 13:08:32 +02:00
DanB
f0fbe79fca Adding test for insufficient balance 2013-12-05 10:44:36 +01:00
DanB
e90960e7fc Merge branch 'master' of https://github.com/cgrates/cgrates 2013-12-04 20:58:18 +01:00
DanB
b5d938a767 Validator bug fixup for DestinationRates 2013-12-04 20:53:38 +01:00
DanB
a3d4b9dd8b Completing validators basic tests 2013-12-04 20:46:39 +01:00
Radu Ioan Fericean
10dea0c55d show error on not enough credit when debiting 2013-12-04 21:26:17 +02:00
DanB
131997507d Testing regexp validators 2013-12-04 20:16:45 +01:00
DanB
086d1e7ab2 Adding load_helper_test file 2013-12-04 17:46:11 +01:00
Radu Ioan Fericean
389a81ae4c better locking for history server 2013-12-04 16:03:53 +02:00
Radu Ioan Fericean
300153f961 better *log action 2013-12-04 12:23:33 +02:00
Radu Ioan Fericean
e6ae067db1 reverted some unnecessary optimization 2013-12-04 12:12:24 +02:00
Radu Ioan Fericean
51379946d9 skip the general balance from counter 2013-12-04 11:58:53 +02:00
Radu Ioan Fericean
95b6cbbdc8 refactored unit counters 2013-12-04 11:44:48 +02:00
DanB
3fb00f8fea Doc formatting fixup 2013-12-03 17:34:09 +01:00
DanB
4de7dd3c26 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-12-03 17:03:46 +01:00
DanB
1388e7caab Testing new doc format for api timings 2013-12-03 17:03:30 +01:00
Radu Ioan Fericean
66c6efab96 fixed debit bug 2013-12-03 17:03:20 +02:00
DanB
92f9849380 Adding realcalls_test file 2013-12-03 15:00:06 +01:00
DanB
5b34dcebd4 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-12-03 12:29:21 +01:00
DanB
14b9c5d6c9 Adding call_url to tests in apier local tests 2013-12-03 12:29:16 +01:00
Radu Ioan Fericean
8482dde7e8 no longer creates an empty balance at account init 2013-12-03 12:46:56 +02:00
DanB
c0a48ad33a Merge branch 'master' of https://github.com/cgrates/cgrates 2013-12-03 11:24:57 +01:00
DanB
f1e4b57b2c Fixup loader_csv to load call_url actions 2013-12-03 11:24:50 +01:00
DanB
2ffaf09ea1 Fixup Actions regexp validation to accept call_url action 2013-12-02 17:37:35 +01:00
Radu Ioan Fericean
51c3a8b2ff better 1.2 msgpack code 2013-12-02 14:44:55 +02:00
Radu Ioan Fericean
eebfa68375 fixed action timing next date bug 2013-12-02 14:21:01 +02:00
DanB
148ef2e5a1 Small fixup at pkg folder 2013-12-02 11:20:05 +01:00
Radu Ioan Fericean
cc27c6a1a1 fixed local test script 2013-11-29 20:25:28 +02:00
DanB
92594be302 Small modifications in debian pkg 2013-11-29 19:23:56 +01:00
Radu Ioan Fericean
bb9f8bfd7b added fixed width format for cdrs and modified test local to call test.sh script 2013-11-29 19:53:50 +02:00
Radu Ioan Fericean
490e8043d6 more indenting 2013-11-29 19:17:14 +02:00
Radu Ioan Fericean
69200d00e8 format console api command response 2013-11-29 19:02:47 +02:00
Radu Ioan Fericean
453f4c9f33 added more safety locking 2013-11-29 18:55:14 +02:00
Radu Ioan Fericean
9527757790 using goroutines for redis history saving 2013-11-29 14:35:12 +02:00
DanB
fc4696cdaf Some more debug messages removal 2013-11-28 18:15:48 +01:00
DanB
0c6e524c78 Removing debug messages for call calculation 2013-11-28 17:58:15 +01:00
DanB
3b8d9c9082 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-11-28 17:50:52 +01:00
DanB
52d91e2fb4 Adding debian packaging rules to run under cgrates user once started 2013-11-28 17:50:45 +01:00
Radu Ioan Fericean
0b5ca3fe36 missed one method call 2013-11-28 18:20:00 +02:00
Radu Ioan Fericean
b669d1d326 Added bool parameter for searching datadb for cached values or not 2013-11-28 18:09:24 +02:00
DanB
6138a12974 Improved logging for the cache system, better help message for console get_cost 2013-11-28 15:42:29 +01:00
DanB
8e92c51da2 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-11-27 19:52:18 +01:00
DanB
8bae6e2de7 Adding get_cost to console commands 2013-11-27 19:44:27 +01:00
Radu Ioan Fericean
859890f0b4 fixed two caching issues 2013-11-27 20:13:25 +02:00
Radu Ioan Fericean
f203feab20 removed alternative redis driver 2013-11-27 18:03:49 +02:00
Radu Ioan Fericean
ed863123f6 renamed flush option to flushdb 2013-11-27 17:48:42 +02:00
DanB
b45200ccd3 Fixup in scenario file 2013-11-27 15:08:22 +01:00
DanB
d4ea254bb5 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-11-27 14:48:19 +01:00
DanB
fef08d8a27 Fixups loader reload cache order and history logs 2013-11-27 14:48:11 +01:00
Radu Ioan Fericean
9c693e21d0 added new dependency to update shell scripts 2013-11-27 15:04:26 +02:00
Radu Ioan Fericean
865c3656fd using hoisie redis driver 2013-11-27 15:02:44 +02:00
Radu Ioan Fericean
bd4a19ae16 copy destinationa and subject to callcost 2013-11-27 15:02:07 +02:00
DanB
c380fcb118 Adding -scheduler parameter on cgr-engine, adding local tests for Apier reload scheduler, reload cache and getCost 2013-11-25 19:19:50 +01:00
DanB
fd12134596 Same bug fix also in loader_db 2013-11-25 16:50:37 +01:00
DanB
83eb650c97 Bug fixup in loader 2013-11-25 16:40:36 +01:00
DanB
472d036d15 Reverting to individual TPRatePlan load 2013-11-25 16:32:24 +01:00
DanB
d3ddffd55e Various bug fixups, LoadRatingProfileFiltered loads now also RatingPlan if defined 2013-11-25 13:22:53 +01:00
DanB
ade79936b5 Adding ApierV1.LoadTariffPlanFromFolder API together with it's local tests 2013-11-24 20:21:03 +01:00
DanB
5ff600fe18 Adding LoadAll method to loader so we can compress the code from Apier 2013-11-24 19:20:35 +01:00
DanB
fcb98adcf7 Buffer writter for multiple values in sql queries, finished storage interface cleanup 2013-11-24 16:31:42 +01:00
DanB
6e65376bec LoadAccountActionsFiltered fixed to load more than one acccountActions profile, added test_local.sh script 2013-11-22 14:14:54 +01:00
DanB
4fb7c404c1 Adding local tests for ApierV1.SetAccountActions 2013-11-21 20:43:41 +01:00
DanB
6215289a37 Adding local tests for ApierV1.TPActionTriggers 2013-11-21 20:17:47 +01:00
DanB
3d523128e2 LoadRatingProfileFiltered can now load more than one ratingProfile 2013-11-21 19:21:43 +01:00
DanB
11932fd58c Adding local tests for ApierV1.TPActionTimings 2013-11-21 19:00:33 +01:00
DanB
5ac4949f88 Adding local tests for ApierV1.TPActions 2013-11-21 18:40:16 +01:00
DanB
4a6ea0ed35 Small code compression in console 2013-11-21 18:04:42 +01:00
DanB
f69af88bd6 Adding local tests for Load*Filtered methods in datadb 2013-11-21 17:59:50 +01:00
DanB
5d0a4cf470 Changing GetRatingProfileByTag into GetRatingProfileFiltered; local tests for ApierV1.SetRatingPlan and ApierV1.SetRatingProfile 2013-11-21 11:52:49 +01:00
DanB
fa6f91f608 ApierV1.TPRatingProfile local tests 2013-11-20 22:38:04 +01:00
DanB
a585f39b04 ApierV1.TPRatingPlan local tests 2013-11-20 19:48:07 +01:00
DanB
66815639af ApierV1.TPDestinationRate local tests, moved local tests into *_local_* files in order to recognize them better 2013-11-20 19:28:39 +01:00
DanB
8d991672ab Adding ApierV1.TMDestination local tests 2013-11-20 18:20:30 +01:00
DanB
52b60db6b2 Adding the apier_test file again 2013-11-20 14:47:21 +01:00
DanB
c7c4aefe0d Apier local tests, added -rater parameter to cgr-engine command to be able to enforce starting of the rater/apier out of command line, useful mostly for test units 2013-11-20 14:42:15 +01:00
DanB
87ea266c4a Go fmt on sources 2013-11-19 20:06:53 +01:00
DanB
a20d6258e2 Loader does not longer needs db connections on -dry_run 2013-11-19 20:02:45 +01:00
DanB
30d91c9747 Completing local tests between load from csv and stordb into redis 2013-11-19 18:12:15 +01:00
DanB
87b6f9c12e Local tests for load from storDb 2013-11-19 17:19:04 +01:00
DanB
a1bbe1c7a6 Dry run for loader 2013-11-19 16:51:53 +01:00
DanB
47415dadfc Adding local tests for TPImporter into stordb 2013-11-19 16:45:12 +01:00
DanB
509f4a87cd Adding local csv loader tests 2013-11-19 15:44:17 +01:00
DanB
58e26a46ce Adding RemTPData SQL tests 2013-11-18 22:13:04 +01:00
DanB
34b577c74d Some more methods cleanup in storDb 2013-11-18 20:30:15 +01:00
DanB
38f6b69193 Removing Exists methods on storDb since they are no longer in use 2013-11-18 20:16:49 +01:00
DanB
d5c81818ee scriptPath in test storage 2013-11-18 20:00:42 +01:00
Radu Ioan Fericean
fb9483693f copy rating info on timespan split 2013-11-18 20:07:10 +02:00
Radu Ioan Fericean
b4f34a77cc added matched subject and matched prefix to timespan 2013-11-18 19:49:55 +02:00
Radu Ioan Fericean
4b6b7758f8 small fix for session manager 2013-11-18 19:38:45 +02:00
Radu Ioan Fericean
a483d2304e tests for rating info activation times 2013-11-18 19:36:54 +02:00
DanB
352540ab04 Adding local tests, available via flag -local 2013-11-18 16:29:25 +01:00
DanB
4e0ccbfa19 Unifying fallback subjects key between csv and db, first match between loaded data from csv and from stordb 2013-11-18 10:28:27 +01:00
DanB
f38f04f6c5 Various loader and apier fixes 2013-11-17 23:06:20 +01:00
DanB
f5257b46a5 Completing TP Apis with SetTPAccountActions with duplications and remove 2013-11-16 20:51:35 +01:00
DanB
b50f950474 ApierV1 TPActionTriggers set and remove implementation 2013-11-16 17:02:27 +01:00
DanB
198b604407 ApierV1 TPActionTimings set with duplicated and remove 2013-11-16 16:39:32 +01:00
DanB
210c52f041 ApierV1 TPActions - remove checking for existing, allow duplicates 2013-11-16 16:21:03 +01:00
DanB
6b5923f86f Merge branch 'master' of https://github.com/cgrates/cgrates 2013-11-15 22:43:24 +01:00
DanB
3a26ba86ea TPRatingProfile data remodeling, apis with remove and set without duplication checking: TPDestinationRates, TPRatingPlans, TPRatingProfiles 2013-11-15 22:42:45 +01:00
Radu Ioan Fericean
9df187dd8e better rating information finding 2013-11-15 20:26:15 +02:00
DanB
9cf1aed058 ApierV1 SetTPRate and RemTPRate implementation 2013-11-15 17:50:53 +01:00
DanB
f904973d25 Adding SetDuration method in RatingSlot 2013-11-15 17:31:17 +01:00
DanB
adf015af5c Merge branch 'master' of https://github.com/cgrates/cgrates 2013-11-15 14:12:50 +01:00
DanB
b95d21fa4e RatingPlan.Timing going private to hide it from jsonrpc 2013-11-15 14:12:38 +01:00
Radu Ioan Fericean
d05cd92a8d added RemKey method for cache 2013-11-15 14:28:57 +02:00
DanB
719ed3ef51 Set on TPDestinations with update on duplicates, remove api for TPDestinations 2013-11-15 12:48:14 +01:00
DanB
751ec1e4c3 Adding remove method on tptimings, modified SetTPTiming to update on exists 2013-11-15 12:00:52 +01:00
DanB
c2af82e8b0 Console command to reload cache, documentation for apier on cache and scheduler, various small fixes 2013-11-14 19:35:26 +01:00
DanB
8b29a35b96 Adding reload_cache command to cgr-console, reload_scheduler fixup 2013-11-14 17:32:47 +01:00
DanB
65d828a2c7 Adding destination and rating plans filtering for automated cache reload from loader 2013-11-14 13:31:30 +01:00
DanB
26bb99bd64 Replacing DESTINATION and RATING_PLAN consts with their prefix versions for compressing code 2013-11-14 13:03:15 +01:00
DanB
408047dd1c Resyncing structs from apis to the ones in storage 2013-11-14 12:58:13 +01:00
Radu Ioan Fericean
c075d8182a fixed field names 2013-11-14 09:41:52 +02:00
Radu Ioan Fericean
d6238ba5f7 moved dataseries to utils 2013-11-14 09:39:00 +02:00
Radu Ioan Fericean
8189bbbf36 using utils types to load data 2013-11-14 09:39:00 +02:00
DanB
ef9f5fe612 ExistsData method on dataDb, ApierV1.ReloadCache method, cgr-loader calling ReloadCache if configured 2013-11-13 19:27:44 +01:00
DanB
4c19a2078b Merge branch 'master' of https://github.com/cgrates/cgrates 2013-11-13 10:09:30 +01:00
DanB
86bcbac221 Adding dataDb checks for destinations and rating plans on csv loader 2013-11-13 10:04:51 +01:00
Radu Ioan Fericean
20ad7a8487 cleaned exists functions 2013-11-13 00:35:29 +02:00
Radu Ioan Fericean
c6752be776 started work on rating plan activation time handling 2013-11-13 00:08:19 +02:00
Radu Ioan Fericean
5e7f7f6663 added get rating plan API method 2013-11-13 00:08:19 +02:00
DanB
13ce865b6f Destinations considered from dataDb in case of missing from storDb on load 2013-11-12 19:30:49 +01:00
DanB
64a0cda8c1 Mediator swapping uuid fixed 2013-11-12 19:06:15 +01:00
DanB
0b1d9c2da4 Reusing of predefined rating plans in rating profiles 2013-11-12 13:43:00 +01:00
DanB
41d0bb80ac Changing DestRateTimings into RatingPlans 2013-11-12 12:25:02 +01:00
DanB
7bf11a216d Changing DestRateTimings into RatingPlans 2013-11-12 12:24:53 +01:00
DanB
bb340210f1 Fixup cgr-spanstrss 2013-11-12 10:34:43 +01:00
DanB
ce1c22f142 Activate precaching only for rater 2013-11-12 10:18:33 +01:00
DanB
3614ed2624 Another small fixup in the documentation 2013-11-10 18:44:33 +01:00
DanB
d7854524c9 Small fixup in the documentation 2013-11-10 18:43:21 +01:00
DanB
6f00f9b4c2 Adding ApierV1.ExportCsvCdrs documentation 2013-11-10 18:40:29 +01:00
DanB
3790591eb2 Fixup of cdrexporter documentation 2013-11-10 17:50:25 +01:00
DanB
99fc749c1c Adding CDRExporter documentation 2013-11-10 17:42:50 +01:00
DanB
194c52db2d Adding test units for CDR CSV exporter 2013-11-10 16:55:52 +01:00
DanB
84fbf30376 Implementation of ApierV1.ExportCsvCdrs 2013-11-10 14:37:20 +01:00
DanB
25db02a6fb Shifting GetAllRatedCdrs into filtered GetRatedCdrs to prepare it for exporter 2013-11-10 09:01:35 +01:00
DanB
ec6de7e901 Implemented GetAllRatedCdr in store, renamed StorCDR into RatedCDR, changed time_answer into answer_timestamp in cdr_primary table for better go compatibility 2013-11-09 10:22:49 +01:00
DanB
4a5d228d81 Adding files containing the storCdr 2013-11-08 18:45:20 +01:00
DanB
092853d571 Adding StorCdr type, removing unused CDR interface methods 2013-11-08 18:37:07 +01:00
DanB
0229adde44 Adding sample cdr generation with curl 2013-11-07 18:21:33 +01:00
DanB
ab68dcab79 Removing section numbering in case of advanced section of the documentation 2013-11-07 17:47:35 +01:00
DanB
09b3de0474 Adding documentation for CDR server 2013-11-07 17:41:48 +01:00
DanB
458a8f8628 Adding datetime support for answer_time in cgrCdr 2013-11-07 17:41:17 +01:00
DanB
3eda5495ea Adding CGR-CDR handler 2013-11-07 12:35:30 +01:00
Radu Ioan Fericean
bb7b144f84 updated rating profile API 2013-11-06 17:06:58 +02:00
Radu Ioan Fericean
b87399c123 fixed build error 2013-11-05 20:38:39 +02:00
Radu Ioan Fericean
455ed9e547 no precaching for loader (precache parameter) 2013-11-05 20:16:39 +02:00
DanB
68a010a476 Minor variable corrections 2013-11-04 20:32:18 +01:00
DanB
131bc22609 Renaming default sample tariff plans to reflect latest code changes 2013-11-04 19:42:05 +01:00
DanB
01005b06f3 Updating storage tables to reflect latest code 2013-10-31 13:42:52 +01:00
Radu Ioan Fericean
3ae3cadfec fixed double prefix bug 2013-10-30 11:21:25 +02:00
Radu Ioan Fericean
70571e15d0 logged precaching error 2013-10-30 11:05:52 +02:00
Radu Ioan Fericean
fb80c6c2a8 destinations using string and compression 2013-10-29 16:57:55 +02:00
Radu Ioan Fericean
4f7501a24c moved alternate redis drivers to alt_redis branch 2013-10-29 16:38:48 +02:00
Radu Ioan Fericean
61d777b1eb a little more logging 2013-10-28 20:05:02 +02:00
Radu Ioan Fericean
848281697e Added pre-cache for destinations and rating plans 2013-10-28 20:02:51 +02:00
Radu Ioan Fericean
d9b0461746 using destination contains prefix method 2013-10-28 18:56:23 +02:00
Radu Ioan Fericean
566447a0a0 re-added caching for destinations and moved cache to storage classes 2013-10-28 15:25:45 +02:00
Radu Ioan Fericean
f965e2ef72 switched to zlib with faster read 2013-10-24 21:01:20 +03:00
Radu Ioan Fericean
386e927487 using gzip instead of snappy 2013-10-24 20:34:15 +03:00
Radu Ioan Fericean
151d42cc7f some test with gzip and zlib 2013-10-24 20:21:07 +03:00
Radu Ioan Fericean
dbed53fbbf rating plan optimization 2013-10-24 19:25:34 +03:00
Radu Ioan Fericean
a4559a68ea rating plan optimization 2013-10-24 19:22:55 +03:00
Radu Ioan Fericean
b343d89293 added rating plans history 2013-10-22 14:13:28 +03:00
Radu Ioan Fericean
60c8469477 added snappy for updates 2013-10-21 22:48:25 +03:00
Radu Ioan Fericean
d8aeb0ea47 added snappy compression for rating plans in redis 2013-10-21 22:45:51 +03:00
Radu Ioan Fericean
0e0bfe7af0 more fixes for msgpack and go 1.1.2 2013-10-21 22:27:24 +03:00
Radu Ioan Fericean
9e7b195ec6 fix for msgpack 2013-10-21 19:42:45 +03:00
Radu Ioan Fericean
3466fa103b refactored rating plans structure 2013-10-21 19:18:56 +03:00
Radu Ioan Fericean
d03ff0a5cd newsubject tests and fixes 2013-10-15 14:56:39 +03:00
Radu Ioan Fericean
0573e98d21 added bitdeli 2013-10-14 21:27:52 +03:00
DanB
dd03d344f6 Docs updates to reflect API changes 2013-10-14 09:53:01 +02:00
Radu Ioan Fericean
e1ce1f5358 fixed radix and redigo 2013-10-12 01:15:28 +03:00
Radu Ioan Fericean
260c27861c Merge branch 'master' into redis 2013-10-11 23:16:34 +03:00
Radu Ioan Fericean
c756c799f5 some redis tests 2013-10-11 23:15:55 +03:00
Radu Ioan Fericean
824302a8f5 Merge branch 'master' of github.com:cgrates/cgrates 2013-10-11 20:18:51 +03:00
Radu Ioan Fericean
d608c6fe07 improved long timespans splitting 2013-10-11 20:15:47 +03:00
DanB
6f8e318bfc TP API docs updated 2013-10-10 13:33:57 +02:00
DanB
f22f5a1bd9 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-10-09 20:34:43 +02:00
DanB
a22260b317 Small fixups 2013-10-09 20:34:39 +02:00
Radu Ioan Fericean
5b4e7a1234 quick fix 2013-10-09 20:05:51 +03:00
Radu Ioan Fericean
54ab6074a6 more loading fixes and more tests 2013-10-09 20:02:13 +03:00
DanB
afe0dbc5cb Cleaning up weight from mysql and sample data files 2013-10-09 11:42:33 +02:00
DanB
c9c4a484eb Fix build error 2013-10-09 11:06:25 +02:00
DanB
9c6de26fa5 DBData encoding configuration 2013-10-09 10:41:51 +02:00
Radu Ioan Fericean
47c06835ea fixed typo 2013-10-08 22:12:34 +03:00
Radu Ioan Fericean
c0accc3d5e better loading (hopefully :) 2013-10-08 21:57:32 +03:00
Radu Ioan Fericean
56b4a3f0a4 fix for bad timespan 2013-10-08 15:27:04 +03:00
Radu Ioan Fericean
48ccb6e930 even more logging 2013-10-08 14:10:17 +03:00
Radu Ioan Fericean
f286e0a189 Merge branch 'master' of github.com:cgrates/cgrates 2013-10-08 13:18:05 +03:00
Radu Ioan Fericean
f2a9154b41 simplified mediator call duration and more logging 2013-10-08 13:16:18 +03:00
DanB
b7a87d1e59 Action bug fixup, fix cost field in stor db 2013-10-08 11:57:06 +02:00
Radu Ioan Fericean
c59dc78f01 get cost call duration fix 2013-10-07 22:28:04 +03:00
Radu Ioan Fericean
88f789cea9 fixed broken test 2013-10-07 20:04:35 +03:00
Radu Ioan Fericean
4428c09748 ore tests and small fixes 2013-10-07 18:44:20 +03:00
Radu Ioan Fericean
97540fcf8c use matched subject in call cost information 2013-10-07 17:49:44 +03:00
Radu Ioan Fericean
617c3f9591 more tests 2013-10-07 17:03:00 +03:00
DanB
036e363ca8 BUG fixup - StorDB close looping 2013-10-07 15:19:59 +02:00
DanB
f1d2bc6164 Renaming RateSubject into RatingSubject to better reflect it's purpose 2013-10-06 19:18:41 +02:00
DanB
df1cd36f50 Bug fixup in mediator not loading the cdrDb 2013-10-06 18:51:37 +02:00
DanB
dbdad3e3ce Fixing default import files, SQL correction in tp_actions tables 2013-10-06 17:34:24 +02:00
Radu Ioan Fericean
483a13f983 fixed span stress 2013-10-06 11:39:07 +03:00
Radu Ioan Fericean
76d7e02402 merged work on new subject debiting 2013-10-06 11:25:23 +03:00
Radu Ioan Fericean
f5059e55fa started minute new subject 2013-10-06 11:21:16 +03:00
Radu Ioan Fericean
33f24534b3 finished split by increment refactoring 2013-10-04 22:15:25 +03:00
Radu Ioan Fericean
85027f0764 new subject for monetary balances 2013-10-04 22:13:37 +03:00
Radu Ioan Fericean
7632a8f14e tests passing on get max session time 2013-10-04 16:18:28 +03:00
Radu Ioan Fericean
c889eafb20 removed special price 2013-10-03 20:14:45 +03:00
Radu Ioan Fericean
8bd4ac1eba msgpack fixes 2013-10-02 22:33:36 +03:00
Radu Ioan Fericean
7db0919be0 started removing special rates and adding new rate subject 2013-10-02 22:30:42 +03:00
Radu Ioan Fericean
fc00528007 removed unused import 2013-10-02 22:12:01 +03:00
Radu Ioan Fericean
5a6df95133 updated msgpack codec 2013-10-02 22:09:21 +03:00
Radu Ioan Fericean
6cd7edd8a2 refound money 2013-10-02 20:09:42 +03:00
Radu Ioan Fericean
09238f7355 spliting for money/minutes separation 2013-10-02 17:30:10 +03:00
Radu Ioan Fericean
552aa0904f using DestinationContainsPrefix method 2013-09-30 22:07:39 +03:00
Radu Ioan Fericean
eec68807ba db loading hopefully the same as csv 2013-09-26 18:47:29 +03:00
Radu Ioan Fericean
94addca3e5 implemented call url action
it will send a POST request to the action ExtraParameters string url with the json encoded user balance as the body
2013-09-26 13:08:03 +03:00
Radu Ioan Fericean
6c35f6e340 improved csv loading 2013-09-25 21:52:06 +03:00
Radu Ioan Fericean
17b982bdeb new debit shaping up 2013-09-24 18:50:44 +03:00
Radu Ioan Fericean
cd98141fbb still working on balance subject debit 2013-09-23 19:26:40 +03:00
Radu Ioan Fericean
3b98a75bab Merge branch 'shared_balances' into redis 2013-09-18 22:52:18 +03:00
Radu Ioan Fericean
8c1ad3de86 reverted to map for tests 2013-09-18 18:09:56 +03:00
Radu Ioan Fericean
f90b9e7aa3 destinations are now stored in sets 2013-09-18 17:55:07 +03:00
Radu Ioan Fericean
9c5715bb7b redis drivers 2013-09-15 23:07:45 +03:00
Radu Ioan Fericean
1ff176cf47 more name refactorings 2013-09-14 20:09:59 +03:00
Radu Ioan Fericean
8cda25cadb one more action 2013-09-14 18:10:00 +03:00
Radu Ioan Fericean
fa788103cf added test for all csv loaders 2013-09-14 17:58:52 +03:00
Radu Ioan Fericean
bb22d37fc6 working on csv load testing 2013-09-14 15:23:52 +03:00
Radu Ioan Fericean
e18420ea23 rated seconds slice 2013-09-12 21:09:42 +03:00
Radu Ioan Fericean
a35f5ed480 interval and activation profile refactoring 2013-09-12 16:24:57 +03:00
Radu Ioan Fericean
a1612f5507 implemented timespan expanding 2013-09-10 14:45:55 +03:00
Radu Ioan Fericean
1ee31ca004 started rating unit implementation 2013-09-09 22:16:07 +03:00
Radu Ioan Fericean
372fbe0e10 started destination hash storage and colapsed debit actions 2013-09-09 20:45:46 +03:00
Radu Ioan Fericean
2f6b1f3f84 tests passing with new balance struct 2013-09-05 23:28:04 +03:00
Radu Ioan Fericean
f3f9d03c80 before switching to MINUTES+OUTBOUND 2013-09-05 20:17:14 +03:00
Radu Ioan Fericean
931c6efb1a converted minutebucket to balance, compiled but test fail 2013-09-05 19:14:22 +03:00
Radu Ioan Fericean
f82a2c58a8 better balance Equal function 2013-09-05 16:55:06 +03:00
Radu Ioan Fericean
48d1953f9a minor doc corrections (hope drone.io will build the master branch if I make a commit on it) 2013-09-04 19:32:42 +03:00
Radu Ioan Fericean
5516c8ff1f started bucket balance merge 2013-09-04 18:18:49 +03:00
Radu Ioan Fericean
3731fb1977 merged storage interfaces split 2013-08-31 13:01:30 +03:00
Radu Ioan Fericean
8f804b8bc5 refactorings, started groups 2013-08-30 19:02:05 +03:00
Radu Ioan Fericean
5083b54bcd removed unused dependencies 2013-08-30 13:31:44 +03:00
Radu Ioan Fericean
637e146ba2 absolute/percent const rename 2013-08-30 13:26:06 +03:00
Radu Ioan Fericean
7edcb9fd6e added round to minute util function and reverted to debit only available credit for prepaid 2013-08-29 15:51:03 +03:00
Radu Ioan Fericean
9004ef1075 using new msgpack package 2013-08-28 23:01:59 +03:00
Radu Ioan Fericean
20f1bf27c3 debit minutes even if balance goes negative 2013-08-28 18:02:54 +03:00
Radu Ioan Fericean
17fe5b3351 rating logic documentation 2013-08-27 19:58:04 +03:00
Radu Ioan Fericean
47442dda3c small typo 2013-08-27 19:01:30 +03:00
Radu Ioan Fericean
ed6c1fc151 logdb, cdrdb and loaddb are all stordb 2013-08-27 16:01:44 +03:00
Radu Ioan Fericean
5acbdc6934 cleaned unimplemented methods from db adaptors 2013-08-27 15:07:50 +03:00
Radu Ioan Fericean
411af6ba74 splited storage interface in multiple interfaces 2013-08-27 14:48:01 +03:00
Radu Ioan Fericean
1bfaa3a52f simplified code and corrected rating profile id 2013-08-26 17:06:30 +03:00
Radu Ioan Fericean
8a6d7a897b Merge branch 'master' of github.com:cgrates/cgrates 2013-08-26 14:13:44 +03:00
Radu Ioan Fericean
9cce7c6449 checking destination ids exist on setting rating profile api call 2013-08-26 14:13:27 +03:00
Radu Ioan Fericean
860f00080d missed reload command file 2013-08-26 10:56:02 +03:00
DanB
3c6b5051af Merge branch 'master' of https://github.com/cgrates/cgrates 2013-08-25 22:32:36 +02:00
DanB
ba3d3c450d TP Docs 2013-08-25 22:32:16 +02:00
Radu Ioan Fericean
c651aabb32 killed internal serialization and started using msgpack 2013-08-23 19:54:06 +03:00
Radu Ioan Fericean
08c4386e65 added reload scheduler to console commands 2013-08-23 17:09:37 +03:00
Radu Ioan Fericean
c3102e8375 reverted too using time sleep instead of a ticker because of the loop execution order 2013-08-23 15:54:29 +03:00
DanB
bf772ada3f Fixup timings 2013-08-22 21:05:03 +02:00
DanB
609c821a50 Adding partial new documentation structure for tariff plans 2013-08-22 20:45:24 +02:00
Radu Ioan Fericean
af64c50fe6 hoping for a minute bucjet fix 2013-08-22 17:08:40 +03:00
Radu Ioan Fericean
2448507578 check the string instead of slice 2013-08-22 16:58:34 +03:00
Radu Ioan Fericean
9409a4b65e more debugging 2013-08-22 16:41:04 +03:00
Radu Ioan Fericean
09bbf2af8f wanted to make sure the log is with the atest version 2013-08-22 16:21:12 +03:00
Radu Ioan Fericean
c2913b6fe9 simplifyed code for minute buckets 2013-08-22 14:36:27 +03:00
Radu Ioan Fericean
99992ab38a small fix for minute buckets serialization 2013-08-22 14:30:26 +03:00
Radu Ioan Fericean
7b559fa68d even better get next date for timed actions 2013-08-22 11:44:03 +03:00
Radu Ioan Fericean
154a9ab00b Merge branch 'master' of github.com:cgrates/cgrates 2013-08-21 19:24:34 +03:00
Radu Ioan Fericean
85a7e434b7 fixed the firts of month bug 2013-08-21 19:24:12 +03:00
DanB
30d731d4c5 Removing ExpiryTime from mandatory Actions parameters since there are specific Actions who do not define it 2013-08-21 14:57:50 +02:00
Radu Ioan Fericean
1c912c8974 fixed typo 2013-08-20 19:57:27 +03:00
Radu Ioan Fericean
c14489222b fix for empty minute buckets 2013-08-20 19:55:21 +03:00
DanB
b3e9a84f6f Fixup ExpiryTime in actions, string now 2013-08-19 18:09:23 +02:00
DanB
86919f831f Bug fixes SQL params 2013-08-18 20:47:19 +02:00
DanB
54704b8cdd Renaming api param RateProfileId into RatingProfileId, bug fixup in sql query related to SetRatingProfile API 2013-08-18 19:55:23 +02:00
DanB
38df076010 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-08-18 17:06:32 +02:00
DanB
edd2c704de TP APIs are now accepting string as time.Duration values, modified mysql table structures to increase *id fields to 64 2013-08-18 17:06:03 +02:00
Radu Ioan Fericean
b894ab8236 faster history record search 2013-08-16 13:46:57 +03:00
DanB
b799dcc8ae BUG Fix SetTPRatingProfile API 2013-08-16 11:40:41 +02:00
Radu Ioan Fericean
c639c13647 travis to use go 1.1 2013-08-14 18:02:25 +03:00
Radu Ioan Fericean
e27bcaabf3 fix on extra pointer and added tests for missing fields checker 2013-08-14 17:53:40 +03:00
DanB
a67e410ed8 History server reconnects 2013-08-12 14:08:47 +02:00
Radu Ioan Fericean
a9f03893b5 doc updates 2013-08-07 22:26:41 +03:00
DanB
d31536c249 Fixup create_tariffplan_tables 2013-08-07 17:47:07 +02:00
Radu Ioan Fericean
48fb53ac08 running load tests 2013-08-07 18:18:44 +03:00
Radu Ioan Fericean
305dca0fc9 some name refactoring 2013-08-07 15:37:17 +03:00
Radu Ioan Fericean
cb20ae167f moved rated units in rate group and transformed it in duration 2013-08-07 15:11:05 +03:00
Radu Ioan Fericean
7a38b39631 more switch relative time fixes 2013-08-07 14:20:14 +03:00
Radu Ioan Fericean
c209dbfb9f using time.Duration for rateincrements and groupinterval 2013-08-07 13:37:25 +03:00
Radu Ioan Fericean
0785874357 call duration fixes and switch relative start/end time 2013-08-07 13:37:25 +03:00
DanB
f07a0cc885 Fixing mediator and related for postpaid rating into storDb 2013-08-07 06:20:56 +02:00
DanB
84dbe167f5 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-08-06 18:09:13 +02:00
Radu Ioan Fericean
e027a2a6a1 nil user balance fix 2013-08-05 18:06:33 +03:00
Radu Ioan Fericean
5dcbb28dd6 added more console commands 2013-08-05 17:58:42 +03:00
Radu Ioan Fericean
9a0dc72d0d added execute action 2013-08-05 17:02:05 +03:00
Radu Ioan Fericean
52afbd376e added versioning for api and two new console commands: get_destination and add_balance (also improved set_balance) 2013-08-05 16:33:47 +03:00
Radu Ioan Fericean
8a58dfd3dd history sync improvements 2013-08-05 12:47:41 +03:00
DanB
be1c2fbb7a Adding freeswitch autoload_configs folder to automatically support json_cdr 2013-08-05 11:39:26 +02:00
DanB
f002c2a224 Updating prepaid1centpersec tariffplan scenario, removing data/rates in favour of tariffplans folder 2013-08-05 11:31:28 +02:00
DanB
609bbdcb2a Merge branch 'master' of https://github.com/cgrates/cgrates 2013-08-04 23:17:34 +02:00
DanB
8000887051 Config and code updates to properly handle CDRS and Mediator variations 2013-08-04 22:01:36 +02:00
DanB
a814b1cc17 Adding sanity checks for CDRS working with internal mediator 2013-08-04 21:48:25 +02:00
Radu Ioan Fericean
2d08f0a31e set -1 cost on error 2013-08-04 22:32:11 +03:00
DanB
b04f276fb6 Removing configuration to enable cdr handlers since filtering is based on url and no extra resources are consumed by handlers 2013-08-04 21:29:51 +02:00
DanB
5f730090fc Adding some formatting 2013-08-04 21:06:43 +02:00
DanB
ff6b55c111 Adding CDRSEnable in config 2013-08-04 21:05:27 +02:00
Radu Ioan Fericean
2156f05131 history flood/loop protection 2013-08-04 20:21:43 +03:00
Radu Ioan Fericean
882a567fe1 added history client for cgr-loader 2013-08-04 18:58:23 +03:00
DanB
58c16a5f47 Errors fixup in sql queries 2013-08-04 16:45:43 +02:00
Radu Ioan Fericean
8b6639f186 better error reporting in the serializer 2013-08-04 15:51:04 +03:00
Radu Ioan Fericean
88db48470d integrated hstory into cgr-engine command 2013-08-04 15:39:53 +03:00
DanB
864b7eca5a Adding extra fields test 2013-08-04 13:42:43 +02:00
DanB
05c6ee4756 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-08-04 13:18:12 +02:00
DanB
d1fd3e4e09 Changing OUT-> *out everywhere, tests fscdr 2013-08-04 13:17:55 +02:00
Radu Ioan Fericean
9411759bc9 added history configuration options 2013-08-03 20:19:05 +03:00
DanB
80e5fa7403 Saving datetime as unixtime in MySQL queries 2013-08-02 22:04:43 +02:00
DanB
06ed29ddd4 Dropping support for urlencode in json fscdr. This can be disabled via FS module 2013-08-02 21:49:45 +02:00
Radu Ioan Fericean
13e8f53230 no longer expand the dates for *any 2013-08-02 19:33:47 +03:00
Radu Ioan Fericean
8c8f003239 added rating profile history handler 2013-08-02 18:57:54 +03:00
DanB
10f46f348a Fixups pkg 2013-08-02 17:36:47 +02:00
DanB
230571c632 Fix run script in the skel folder 2013-08-02 17:10:57 +02:00
Radu Ioan Fericean
8fac5a1ff1 cstmid to tenant 2013-08-02 17:46:07 +03:00
Radu Ioan Fericean
13347532d6 url decode extra fields 2013-08-02 17:13:02 +03:00
Radu Ioan Fericean
b6b5c11033 same OUT problem with cdrs 2013-08-02 17:03:15 +03:00
Radu Ioan Fericean
4a9c4eff43 fs event returning *out 2013-08-02 17:01:42 +03:00
Radu Ioan Fericean
d0e185c47a one history recor per line 2013-08-02 15:17:55 +03:00
Radu Ioan Fericean
457151ecce added history tests 2013-08-02 13:05:19 +03:00
Radu Ioan Fericean
ed5ebc2efc added some more tests 2013-08-02 12:58:33 +03:00
Radu Ioan Fericean
1771d9de3a history test passing 2013-08-02 12:54:35 +03:00
Radu Ioan Fericean
b57d321e30 made test pass for the moment 2013-08-01 18:20:41 +03:00
Radu Ioan Fericean
5941823138 Merge branch 'master' of github.com:cgrates/cgrates 2013-08-01 18:20:17 +03:00
Radu Ioan Fericean
6c5d73d41a more refactoring and added first test 2013-08-01 18:19:18 +03:00
Radu Ioan Fericean
63a1fc6f31 refeactored history 2013-08-01 17:25:26 +03:00
Radu Ioan Fericean
8a457618be working on store 2013-08-01 17:03:22 +03:00
DanB
5bb67111b2 Fixup packaging script 2013-08-01 08:59:46 +02:00
DanB
1b759af9e2 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-08-01 08:20:45 +02:00
DanB
e5652558bc Adding debian packaging script 2013-08-01 08:16:03 +02:00
Radu Ioan Fericean
606d053844 better parse date 2013-07-31 14:45:46 +03:00
Radu Ioan Fericean
59e474bf68 expiration time fixes 2013-07-31 10:05:13 +03:00
Radu Ioan Fericean
1d3ce69c5d added activation time with + and * stuff 2013-07-30 23:06:31 +03:00
Radu Ioan Fericean
11e7eed149 added +duration and *monthly for expiration time 2013-07-30 22:42:51 +03:00
Radu Ioan Fericean
78d2ee4e28 fixed tests 2013-07-30 22:15:35 +03:00
Radu Ioan Fericean
67d9c6f64d merge Dan's work 2013-07-30 21:54:27 +03:00
Radu Ioan Fericean
d112b53dda working on duration expire time 2013-07-30 21:47:17 +03:00
DanB
2f733525b2 Finalizing TPCSVImporter with AccountActions method 2013-07-30 19:35:20 +02:00
DanB
c60b26c0c4 TPCSVImporter ActionTriggers 2013-07-30 17:03:16 +02:00
DanB
d483a6669f TPCSVImporter ActionTimings 2013-07-30 12:52:58 +02:00
DanB
d4017890ec TPCSVImporter Actions, store refactoring, small API params changes 2013-07-29 21:18:02 +02:00
DanB
ddc2240c67 TPCSVImporter RatingProfiles, changed RateProfile into RatingProfile in APIs for consistency 2013-07-29 18:21:13 +02:00
DanB
9887799492 TPCSVImporter DestRateTiming, store refactoring 2013-07-29 15:32:07 +02:00
DanB
f1d94aebaf TPCSVImporter - DestinationRates implementation 2013-07-29 14:18:50 +02:00
DanB
1a1403ace1 Few more methods on TPCSVImporter, API TPRates modifications to include GroupInterval 2013-07-29 12:40:23 +02:00
DanB
63d9612932 Another way of mapping csvtpimport function, trying to fix tests on 1.0.3 2013-07-27 13:29:34 +02:00
DanB
37c893d4f8 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-07-27 12:26:57 +02:00
DanB
ee15ce5056 Tests fix 2013-07-27 12:26:42 +02:00
DanB
0be3f8b342 Small fixups cgr-loader 2013-07-27 12:09:07 +02:00
DanB
26d270c7c1 Updating cgr-loader to dataDb 2013-07-27 11:39:31 +02:00
DanB
e5f0af9fb6 Refactoring validators to match new csv file structure 2013-07-27 10:25:18 +02:00
DanB
9f28fa0d24 Partial TPCSVImporter, TPCSVFileParser 2013-07-25 20:29:47 +02:00
Radu Ioan Fericean
ff9f8c82af started work on generic cdr 2013-07-25 13:51:22 +03:00
Radu Ioan Fericean
64c1cd67f0 implemented global rounding method and precision for get cost 2013-07-25 12:16:15 +03:00
Radu Ioan Fericean
6afd7ee19f one more test for group split 2013-07-24 19:10:50 +03:00
Radu Ioan Fericean
946a496a46 more tests for splitting 2013-07-24 19:08:51 +03:00
Radu Ioan Fericean
af811d669e moved rate increments in the group scope 2013-07-24 14:13:58 +03:00
DanB
352b58eccc Engine path in daemontools script 2013-07-24 12:54:17 +02:00
Radu Ioan Fericean
c1139bc2a8 changed importer to to reflect new package name 2013-07-24 13:41:57 +03:00
Radu Ioan Fericean
6c79fd2092 rater lib changed to engine 2013-07-24 13:38:45 +03:00
Radu Ioan Fericean
3d9fc28d82 changed cgr-rater tool name to cgr-engine 2013-07-24 13:13:08 +03:00
DanB
14adc49f2e Partial TPImporter implementation 2013-07-24 12:08:58 +02:00
DanB
3ddc63f4f5 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-07-24 09:29:29 +02:00
Radu Ioan Fericean
42cb2d9b4f goup rates (not fully tested) 2013-07-23 17:33:17 +03:00
DanB
10b40e2652 StorDb defaults to MYSQL 2013-07-23 16:28:26 +02:00
Radu Ioan Fericean
c14be129ed loading rates 2013-07-23 16:58:00 +03:00
Radu Ioan Fericean
6d3e011634 some rating optimization and all hardcoded values using *lowercase 2013-07-23 16:58:00 +03:00
DanB
2c7eb3593e New config parameters - RaterRoundingMethod and RaterRoundingDecimals, renaming logDb into storDb in config 2013-07-23 13:59:17 +02:00
DanB
8845b4b310 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-07-23 11:23:19 +02:00
DanB
84acdd737b Centralizing ConfigureDatabase and csv filenames 2013-07-23 11:23:11 +02:00
Radu Ioan Fericean
2805190f45 small management api improvements 2013-07-22 13:54:30 +03:00
Radu Ioan Fericean
bcc0370c31 fix for setaction on non exiting account 2013-07-22 12:36:00 +03:00
DanB
eb906ed87a Fixup 2013-07-20 22:15:20 +02:00
DanB
081ddce3f3 Formatting SetAccountActions api documentation 2013-07-20 22:05:19 +02:00
DanB
06fef1536c SetAccountActions API fixups 2013-07-20 18:34:37 +02:00
DanB
77abee97db Adding TPAccountActions API documentation 2013-07-20 14:29:44 +02:00
DanB
e4530fdd0e Adding TPAccountActions api methods 2013-07-20 13:59:13 +02:00
DanB
22f9297248 Adding ActionTriggers APIs documentation 2013-07-19 20:12:35 +02:00
DanB
30de8c1c95 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-07-19 19:49:50 +02:00
DanB
b8a9d77575 Adding ActionTriggers api methods 2013-07-19 19:49:44 +02:00
Radu Ioan Fericean
383bba3412 small docs fix 2013-07-19 20:16:24 +03:00
Radu Ioan Fericean
210f290e67 SetAccountAction API and filter for RESET_TRIGGERS 2013-07-19 20:06:42 +03:00
Radu Ioan Fericean
6c8d2c7187 improved expiration time 2013-07-19 20:06:41 +03:00
DanB
17597375dc Adding ActionTimings api documentation 2013-07-19 10:47:51 +02:00
DanB
092a80d9d4 Adding ActionTimings api methods 2013-07-19 09:37:07 +02:00
DanB
e7de9222d1 Reversing the order of ThresholdValue and ThresholdType definition in csv files 2013-07-18 14:16:02 +02:00
DanB
39923c3d5e Merge branch 'master' of https://github.com/cgrates/cgrates 2013-07-18 14:04:26 +02:00
DanB
6969606096 Actions related API data definition 2013-07-18 13:59:47 +02:00
Radu Ioan Fericean
d5c4c717f3 rounding methods in csv loader and implemented triggered type 2013-07-18 13:44:19 +03:00
DanB
72fd2984ad Unique keys for actiontriggers and account actions 2013-07-18 12:30:30 +02:00
DanB
d75d1475fc Adding mysql unique key for actions 2013-07-18 12:01:43 +02:00
DanB
2eb8c093ad Finishing TPActions API methods and adding their documentation 2013-07-18 11:36:45 +02:00
DanB
0a3adf326e Adding SetTPAction api method 2013-07-17 19:37:48 +02:00
DanB
c0881d977d Adding GetDestination api back 2013-07-17 14:55:30 +02:00
DanB
09f5db1a31 Fixup doc 2013-07-17 14:41:21 +02:00
DanB
e29f295cb0 Adding SetRatingProfile API documentation 2013-07-17 14:36:19 +02:00
DanB
5ba6c6ccc9 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-07-16 21:18:23 +02:00
DanB
ebbadb50dd Apier.SetRatingProfile method fixups 2013-07-16 21:18:08 +02:00
Radu Ioan Fericean
b6bfc1745c expiration balances and minute buckets 2013-07-15 21:06:14 +03:00
DanB
8f3b146f71 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-07-15 15:26:47 +02:00
Radu Ioan Fericean
6d595eee2d expiration date for minutebuckets 2013-07-15 12:53:46 +03:00
Radu Ioan Fericean
6f0ba9449b reverted multiple destinations 2013-07-15 11:33:42 +03:00
Radu Ioan Fericean
6aafc8cf5f working on dimed balances 2013-07-15 11:31:36 +03:00
Radu Ioan Fericean
45fd41a1a2 Implement balance counters for number of minutes with a special discount per group of destinations 2013-07-14 20:29:40 +03:00
Radu Ioan Fericean
460b2470f1 Merge branch 'master' of github.com:cgrates/cgrates 2013-07-14 17:59:22 +03:00
Radu Ioan Fericean
ae3a6a786e rounding methods and decimals for interval 2013-07-14 17:59:14 +03:00
DanB
c39b2d5a84 Fixup docs 2013-07-12 20:41:49 +02:00
DanB
2be2bc97a0 Adding API support for rounding functions at rate level 2013-07-12 16:09:41 +02:00
DanB
526b50ed12 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-07-12 11:26:17 +02:00
DanB
1ada3d929a Adding TPRateProfile documentation 2013-07-12 11:26:09 +02:00
Radu Ioan Fericean
3123b44501 Merge branch 'master' of github.com:cgrates/cgrates 2013-07-12 12:24:08 +03:00
Radu Ioan Fericean
34bc6107ea use rating profile id for loading from storedb 2013-07-12 12:23:56 +03:00
DanB
b7c6314a09 Finished implementing TPRateProfile APIs 2013-07-12 08:40:28 +02:00
DanB
7b1b278c87 Adding SetRatingProfile api method 2013-07-11 21:34:31 +02:00
DanB
aa1c2021d3 Adding TPDestRateTiming APIs and attached documentation 2013-07-11 14:44:54 +02:00
DanB
341c96552d Fixup doc 2013-07-10 17:34:25 +02:00
DanB
89af3fa82e Adding TPDestinationRates API documentation 2013-07-10 17:31:44 +02:00
DanB
f3dbbf77fc Adding TPDestinationRate APIs 2013-07-10 16:58:57 +02:00
DanB
33febd9285 Fixup documentation tprates 2013-07-10 15:20:16 +02:00
DanB
edbfd1b436 loader_csv fixup for tests to pass 2013-07-10 12:46:02 +02:00
DanB
57c47987e2 Adding Apier TPRates documentation 2013-07-10 12:23:51 +02:00
DanB
f443d384d0 Adding Apier Get methods for TPRate 2013-07-10 11:55:12 +02:00
DanB
d3e16d3712 Apier.SetTPRates with RateSlots 2013-07-10 10:36:51 +02:00
DanB
60bd0c0a08 Implemented Apier.SetTPRate method 2013-07-10 08:13:09 +02:00
DanB
b5092ebabe Implemented Apier.SetTPRate method 2013-07-10 08:13:04 +02:00
DanB
2b025a3fc9 Adding TP APIs 2013-07-09 15:48:49 +02:00
DanB
2aaac1b9e1 Fixup doc 2013-07-09 13:28:19 +02:00
DanB
b5a5b4631f Fixup doc 2013-07-09 13:27:24 +02:00
DanB
49ecf65fdc Refactoring TPDestination Api documentation 2013-07-09 13:24:26 +02:00
DanB
8ecf706086 Adding TPTiming APIs together with documentation attached 2013-07-09 13:15:02 +02:00
DanB
7d06363c10 Adding attrs structure to getTpDestinationIds 2013-07-08 17:38:34 +02:00
DanB
dfa91442d4 Reverting tpdestinations 2013-07-08 17:32:36 +02:00
DanB
d2baf4fa08 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-07-08 15:16:11 +02:00
DanB
56b5ce9a49 Refactoring tpdestination documentation based on new data template 2013-07-08 15:15:53 +02:00
Radu Ioan Fericean
0401809d0d fix for rates loading 2013-07-08 14:45:19 +03:00
DanB
f0d96157e4 Adding documentation for Apier.GetTPDestination and Apier.GetTPDestinationIds methods 2013-07-07 20:45:41 +02:00
DanB
6152ce6925 Merge branch 'master' of https://github.com/cgrates/cgrates 2013-07-07 19:46:28 +02:00
DanB
5d62eea4f2 Adding Apier.SetTPDestination documentation 2013-07-07 19:46:10 +02:00
Radu Ioan Fericean
2ed059ff7d loading rates from db 2013-07-07 14:10:07 +03:00
Radu Ioan Fericean
389c173471 csv loading rates 2013-07-06 12:04:01 +03:00
DanB
25df692872 Adding GetTPDestinationIds API method 2013-07-05 08:46:54 +02:00
DanB
db62f2cc85 Finishing Get and Set for TPDestinations 2013-07-04 19:20:55 +02:00
DanB
7e308dca52 Adding Apier.SetTPDestination 2013-07-03 23:01:37 +02:00
Radu Ioan Fericean
00e48bc23a new setratingprofile api command 2013-07-03 14:24:56 +03:00
Radu Ioan Fericean
8a7d8c7fd1 fixed typo 2013-07-01 15:42:48 +03:00
Radu Ioan Fericean
f89eecaef8 writing the loaded rating profile to db 2013-06-29 17:35:27 +03:00
Radu Ioan Fericean
14ea78cc34 added sql storage 2013-06-28 22:54:34 +03:00
Radu Ioan Fericean
aff4813757 unified sql storages and worked on rating profile load by tag 2013-06-28 22:47:39 +03:00
Radu Ioan Fericean
15e5709e29 started cdrexporter and fallback list by ; not | 2013-06-26 18:33:46 +03:00
Radu Ioan Fericean
3380bf8492 fallback subject list with | 2013-06-26 17:56:15 +03:00
Radu Ioan Fericean
088911a5c8 added extra fields to cdrs 2013-06-26 17:15:15 +03:00
Radu Ioan Fericean
096818feb2 AddAccount API method 2013-06-19 18:35:32 +03:00
Radu Ioan Fericean
070df3fef0 typo fix 2013-06-18 12:32:08 +03:00
Radu Ioan Fericean
a2cae467fe updated docs 2013-06-18 12:28:44 +03:00
Radu Ioan Fericean
61ec52c951 fixses for get/set destinations 2013-06-18 12:12:36 +03:00
Radu Ioan Fericean
609e1213c3 added AddTriggeredAction 2013-06-18 11:18:54 +03:00
Radu Ioan Fericean
d48bd013b7 added tennant and other fields that compose account names 2013-06-17 18:02:10 +03:00
Radu Ioan Fericean
ee25f00d85 docs fixes 2013-06-17 16:34:54 +03:00
Radu Ioan Fericean
e1636b3973 small fix 2013-06-17 16:32:47 +03:00
Radu Ioan Fericean
c9885fad86 added API for destinations 2013-06-17 16:31:15 +03:00
Radu Ioan Fericean
139bab220c started API documentation 2013-06-17 08:49:24 +03:00
Radu Ioan Fericean
ba2665a9a8 more API methods, docs pending 2013-06-16 23:26:40 +03:00
Radu Ioan Fericean
74b7836937 working on apier 2013-06-16 08:12:26 +03:00
Radu Ioan Fericean
3fcf811eab started to add apier methods 2013-06-14 20:40:09 +03:00
Radu Ioan Fericean
52616d09e6 loading from database code complete, needs testing 2013-06-14 15:39:02 +03:00
Radu Ioan Fericean
c8f61690b4 left only one redis storage implementation 2013-06-14 12:59:22 +03:00
DanB
8417f8c581 Removing tpid api since we do not longer need it 2013-06-12 13:22:32 +02:00
DanB
ee7bd686bd Adding NOT_IMPLEMENTED error at storage level 2013-06-12 12:33:06 +02:00
DanB
a254242b46 Adding GetTPDestinations API and storage method 2013-06-12 12:27:30 +02:00
DanB
dd14b7e210 Modifying int into float for tp_tables 2013-06-11 16:21:33 +02:00
DanB
0fbd35627a Moving GenUUID into utils 2013-06-11 16:09:35 +02:00
DanB
198a53b7ed Cleanup automatically added swap files 2013-06-11 13:35:28 +02:00
DanB
e703c3f431 Adding Apier, Apier.GetTPID initial implementation 2013-06-11 13:25:57 +02:00
Radu Ioan Fericean
d4130df421 more work on destinations 2013-06-10 11:15:31 +03:00
Radu Ioan Fericean
76001c74db better API docs links 2013-06-10 10:44:54 +03:00
Radu Ioan Fericean
a6ce08d8b4 started importing and management API 2013-06-10 10:38:24 +03:00
Radu Ioan Fericean
7c811469ab fixed build error 2013-06-08 20:12:20 +03:00
Radu Ioan Fericean
aca4c5a861 first draft of DataStorage loading 2013-06-08 19:12:24 +03:00
Radu Ioan Fericean
7c57d3a321 moved loader to datastorage interface all the way to rating profiles 2013-06-08 01:33:58 +03:00
Radu Ioan Fericean
8f692b5c9e fixed loading issue 2013-06-06 16:43:33 +03:00
Radu Ioan Fericean
b8ab0c9657 reverted to my marshaller 2013-06-06 16:00:52 +03:00
DanB
dd096da7e6 Adding mediator/loadConfig mechanism description 2013-06-06 13:55:24 +02:00
DanB
a75c2e7324 Mediator refactoring to support CDR as imput instead of csv row - adding separation of names and indexes, StartTime->AnswerTime, adding reqtype=rated 2013-06-06 12:37:36 +02:00
Radu Ioan Fericean
fbbb26fab3 Merge branch 'master' of github.com:cgrates/cgrates 2013-06-03 10:42:57 +03:00
DanB
81e2c8bd4e Initial modifications towards a new Mediator structure. Tests and compilation will fail until Mediator fixed 2013-06-02 15:07:03 +02:00
DanB
8ac8eae692 Modifying mediator configuration to match the new architecture required to work with both file and database cdrs 2013-06-02 15:06:16 +02:00
Radu Ioan Fericean
dca4cd0038 added db loader 2013-06-01 16:45:37 +03:00
DanB
3d1656efdb Modifying config file with new mediation fields 2013-05-31 17:18:46 +02:00
Radu Ioan Fericean
ea15c17832 added binc marshaller and added actions 2013-05-31 17:02:21 +03:00
Radu Ioan Fericean
84f489a73a small fixses 2013-05-30 18:26:27 +03:00
DanB
6966a85e68 Refactoring transactions into costdetails table, due to internal concepts 2013-05-30 13:29:28 +02:00
DanB
c06b1e3c59 Adding renaming callcosts into transactions table 2013-05-30 13:00:24 +02:00
Radu Ioan Fericean
92ccdc7fe5 addes store for freeswitch cdr 2013-05-30 13:03:19 +03:00
Radu Ioan Fericean
fed39c6fdd simplified my marshaller 2013-05-30 12:49:06 +03:00
Radu Ioan Fericean
c70d54d790 Merge branch 'master' into marsh 2013-05-30 10:20:03 +03:00
Radu Ioan Fericean
4e7dc77db9 get cost from call cost 2013-05-29 19:21:24 +03:00
Radu Ioan Fericean
dacaa14086 removed rate 2013-05-29 19:19:21 +03:00
Radu Ioan Fericean
bd74e573b9 added cost to rated cdrs 2013-05-29 19:16:39 +03:00
DanB
72e054a9b3 Adding tariffplan tables 2013-05-29 15:05:28 +02:00
DanB
6875f11c35 Replacing GetEndTime with GetDuration, support for MySQL as LogDB 2013-05-28 16:30:29 +02:00
DanB
ef6b1cc9f8 Adding some more tables on mysql side 2013-05-28 16:29:33 +02:00
DanB
f0e82ba4af Refactoring startCDRS to be in trend with the rest of start functions 2013-05-28 14:38:23 +02:00
DanB
a2afe0794a Fixup timestamps source in cdrs, modified cmd/cgr-rater to support disabling mediator as service in configuration 2013-05-28 14:06:08 +02:00
DanB
af97063031 Adding missing values out of fscdr 2013-05-27 17:49:35 +02:00
Radu Ioan Fericean
cd8509084b cdrs before testing 2013-05-26 21:16:00 +03:00
Radu Ioan Fericean
6345b78a44 cdr server capturing events 2013-05-26 18:24:05 +03:00
Radu Ioan Fericean
ba6fd4ae83 started 2013-05-24 10:37:40 +03:00
Radu Ioan Fericean
1c943ff6a3 reverted to standard things 2013-05-23 16:56:11 +03:00
Radu Ioan Fericean
38d951437f mediator parse only non-csv files 2013-05-23 16:52:44 +03:00
Radu Ioan Fericean
84720809ba syslog logging 2013-05-23 15:52:18 +03:00
Radu Ioan Fericean
e40f4349a8 replace inotify with fsnotify 2013-05-23 15:36:53 +03:00
DanB
eaf432e3ad Merge branch 'master' of https://github.com/cgrates/cgrates 2013-05-23 14:23:59 +02:00
DanB
147aef6dc5 Adding mediation tables, modified cdrs_primary to include source host of the CDR 2013-05-23 14:23:52 +02:00
DanB
e3154f5824 Adding new parameters to config, improved testing of config 2013-05-23 13:43:33 +02:00
Radu Ioan Fericean
12d7312853 travis is back 2013-05-23 12:42:32 +03:00
Radu Ioan Fericean
a68fc52891 please travis 2013-05-23 12:39:14 +03:00
Radu Ioan Fericean
d8194b95cf travis last try 2013-05-23 12:37:40 +03:00
Radu Ioan Fericean
0e856b7d80 switched to drone.io 2013-05-23 12:35:35 +03:00
Radu Ioan Fericean
d1af53fb1b intentional test fail to test ci 2013-05-23 12:29:26 +03:00
Radu Ioan Fericean
ef8e2f32b3 more travis 2013-05-23 12:10:24 +03:00
Radu Ioan Fericean
913f28a5f6 test travis 2013-05-23 12:05:38 +03:00
Radu Ioan Fericean
62511ee9e3 removed name from cdr and it should compile on go1.0.x now 2013-05-23 11:36:09 +03:00
DanB
83ef5b6d6f Adding CDRS configuration, better handling and testing of the defaults 2013-05-22 12:32:30 +02:00
DanB
08184114ab Renaming internal variables for json_cdr 2013-05-21 17:04:24 +02:00
Radu Ioan Fericean
fe1790b1f7 fixed test compilation error 2013-05-21 15:09:23 +03:00
Radu Ioan Fericean
bcd6d20197 Merge branch 'cdrs' 2013-05-21 15:03:44 +03:00
DanB
52376fadd0 Adding FreeSWITCH tutorial to documentation 2013-05-21 13:27:55 +02:00
DanB
6d28da7d64 Removing unused configs 2013-05-21 12:40:05 +02:00
DanB
afac0bdfc7 Fixup config files 2013-05-21 12:38:23 +02:00
Radu Ioan Fericean
5f1c23e4ee created a misc tools folder 2013-05-19 20:31:10 +03:00
Radu Ioan Fericean
aa3e3941ed added cdr server to cdr-rater tool 2013-05-19 20:27:25 +03:00
Radu Ioan Fericean
d7f666e323 more refactorings and cdr test 2013-05-19 20:06:08 +03:00
Radu Ioan Fericean
cdfea5aba4 code reorganization and added cdr interface 2013-05-19 15:57:09 +03:00
Radu Ioan Fericean
faaca5ae57 simplifyed fsevent 2013-05-19 14:47:13 +03:00
DanB
dc6c63df36 Adding cdrs tables 2013-05-17 13:49:17 +02:00
Radu Ioan Fericean
28bca0efcc fixed mongo storage 2013-05-17 14:46:33 +03:00
Radu Ioan Fericean
6e2b741028 renamed packet 2013-05-17 12:02:38 +03:00
Radu Ioan Fericean
f31c5b6476 mysql storage 2013-05-17 11:59:20 +03:00
Radu Ioan Fericean
fe4d82b30a added cdr server files 2013-05-17 11:55:07 +03:00
Radu Ioan Fericean
96bba4eb6f started work on online cdr processing 2013-05-16 19:01:06 +03:00
Radu Ioan Fericean
7e7ab83bd8 better test for goci.me 2013-05-16 17:11:27 +03:00
Radu Ioan Fericean
6776525c97 Install packages that are dependencies of the test first 2013-05-14 14:25:22 +03:00
Radu Ioan Fericean
cd32402449 removed specific databases from tests name 2013-05-12 14:45:26 +03:00
Radu Ioan Fericean
48b24d6cff type an error on database not accessible from tests 2013-05-12 14:33:22 +03:00
Radu Ioan Fericean
839b3b8615 added one more driver for redis 2013-05-12 13:58:27 +03:00
Radu Ioan Fericean
72d2d8182c map storage for tests 2013-05-12 12:36:31 +03:00
Radu Ioan Fericean
a7fb55792f more redis corrections 2013-05-12 12:32:37 +03:00
Radu Ioan Fericean
e8edee00c1 Merge branch 'master' of github.com:cgrates/cgrates 2013-05-10 18:28:45 +03:00
Radu Ioan Fericean
5829ab6386 preventing test crashes on missing database systems 2013-05-10 18:16:16 +03:00
DanB
8473840446 Adding reconnect to both Mediator and SessionManager 2013-05-10 13:07:20 +02:00
DanB
25cbad0a90 <cmd/cgr-rater> Introducing synchronicity for config phase to avoid concurrency issues 2013-05-10 12:16:41 +02:00
Radu Ioan Fericean
e5ad2bd09b avoid var hiding 2013-05-09 16:02:02 +03:00
Radu Ioan Fericean
373d97e91d simplifyed redis returned error 2013-05-09 15:27:22 +03:00
Radu Ioan Fericean
4fb47a0a7e only send auth and db if relevant 2013-05-09 15:25:11 +03:00
Radu Ioan Fericean
988ab87192 better db and auth 2013-05-09 13:12:35 +03:00
Radu Ioan Fericean
2c961510ce added update external libs shell script 2013-05-09 12:11:29 +03:00
Radu Ioan Fericean
146a4d6994 fixed radix interface change 2013-05-09 11:18:43 +03:00
DanB
5183307bd9 Small misspell in documentation fixed 2013-04-22 21:54:25 +02:00
DanB
c43d232857 Adding documentation about how to install from debian packages 2013-04-22 21:52:03 +02:00
564 changed files with 81686 additions and 10881 deletions

5
.gitignore vendored
View File

@@ -9,3 +9,8 @@ a.out
docs/_*
bin
.idea
dean*
data/vagrant/.vagrant
data/vagrant/vagrant_ansible_inventory_default
data/tutorials/fs_evsock/freeswitch/etc/freeswitch/
vendor

View File

@@ -1,6 +1,10 @@
language: go
before_install: sudo apt-get -q install bzr
go:
- 1.4
- 1.5
script: $TRAVIS_BUILD_DIR/test.sh
branches:
only: master
@@ -12,8 +16,5 @@ notifications:
on_success: change
on_failure: always
email:
on_success: change
on_success: never
on_failure: always
after_script:
./build.sh && ./test.sh

26
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,26 @@
## Contributor Agreement (required for pull requests)
While the CGRateS project is open source under the GPLv3 license, contributions
back to this project which contain code must be made available to ITsysCOM GmbH under the terms of the MIT license.
### What does this mean?
This means ITsysCOM GmbH can do the following with code contributed
back to the project from the open source community.
1. Include the code in future CGRateS releases.
2. Include the code, in original or modified form, in other products created by ITsysCOM GmbH, **including closed-source products**.
### How do I agree to these terms?
To agree to these terms, sign the contributing agreement in `CONTRIBUTORS.md` and
include the change as a commit with your pull request. **Pull requests which contain
commits from users not explicitly listed in the `CONTRIBUTORS.md` file will be
rejected *without review*.**
* You only need to sign the `CONTRIBUTORS.md` file prior to the first time you
submit a pull request (or include it as part of the pull request itself).
* The submitter of a pull request which adds a user to the `CONTRIBUTORS.md` file
*must* match the user information which was added to the `CONTRIBUTORS.md` file.
For example, only @danbogos can send a pull request containing a commit which adds
@danbogos to the `CONTRIBUTORS.md` file.

42
CONTRIBUTORS.md Normal file
View File

@@ -0,0 +1,42 @@
## Contributors
By signing this agreement, the undersigned agree that contributions made to the CGRateS
project are provided under the terms of the MIT license, included below. For additional
information, please see the [`CONTRIBUTING.md`](CONTRIBUTING.md) file.
**Pull requests which add a user to this file may only be submitted by the user who was added to the file.**
### Contributor License Text
> The MIT License (MIT)
>
> Copyright &copy; &lt;year&gt; &lt;copyright holders&gt;
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> THE SOFTWARE.
### Undersigned
| GitHub Username | Full Name |
| --------------- | --------- |
| @danbogos | Dan Christian Bogos |
| @eloycoto | Eloy Coto Pereiro |
<!-- to sign, include a single line above this comment containing the following text:
| @username | First Last |
-->

View File

@@ -1,6 +1,8 @@
# Rating system for Telecom & ISP environments #
## Real-time Charging System for Telecom & ISP environments ##
## Features ##
[![Build Status](https://secure.travis-ci.org/cgrates/cgrates.png)](http://travis-ci.org/cgrates/cgrates)
### Features ###
+ Rates for prepaid and for postpaid
+ The budget expressed in money and/or minutes (seconds)
+ High accuracy rating: configurable to milliseconds
@@ -11,13 +13,16 @@
+ Good documentation
+ Commercial support available
## Documentation ##
Browsable HTML http://readthedocs.org/docs/cgrates/
### Documentation ###
[Step by steps tutorials](https://cgrates.readthedocs.org/en/latest/tut_freeswitch.html)
[Debian apt-get repository](https://cgrates.readthedocs.org/en/latest/tut_freeswitch_installs.html#cgrates)
Browsable HTML docs http://readthedocs.org/docs/cgrates/
PDF, Epub, Manpage http://readthedocs.org/projects/cgrates/downloads/
API reference http://gopkgdoc.appspot.com/pkg/github.com/cgrates/cgrates
API reference [godoc](http://godoc.org/github.com/cgrates/cgrates/apier)
Also check irc.freenode.net#cgrates and [Google group](https://groups.google.com/forum/#!forum/cgrates) for a more real-time support.
Also check [irc.freenode.net #cgrates](irc://irc.freenode.net:6667/cgrates) ([Webchat](http://webchat.freenode.net?randomnick=1&channels=%23cgrates)) and [Google group](https://groups.google.com/forum/#!forum/cgrates) for a more real-time support.
Continous integration: [![Build Status](https://goci.herokuapp.com/project/image/github.com/cgrates/cgrates "Continous integration")](http://goci.me/project/github.com/cgrates/cgrates) [![Build Status](https://secure.travis-ci.org/cgrates/cgrates.png)](http://travis-ci.org/cgrates/cgrates)

286
apier/v1/accounts.go Normal file
View File

@@ -0,0 +1,286 @@
/*
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 v1
import (
"math"
"regexp"
"strings"
"time"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
type AttrAcntAction struct {
Tenant string
Account string
Direction string
}
type AccountActionTiming struct {
ActionPlanId string // The id of the ActionPlanId profile attached to the account
Uuid string // The id to reference this particular ActionTiming
ActionsId string // The id of actions which will be executed
NextExecTime time.Time // Next execution time
}
func (self *ApierV1) GetAccountActionPlan(attrs AttrAcntAction, reply *[]*AccountActionTiming) error {
if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "Account", "Direction"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(strings.Join(missing, ","), "")
}
accountATs := make([]*AccountActionTiming, 0)
allATs, err := self.RatingDb.GetAllActionPlans()
if err != nil {
return utils.NewErrServerError(err)
}
for _, ats := range allATs {
for _, at := range ats {
if utils.IsSliceMember(at.AccountIds, utils.AccountKey(attrs.Tenant, attrs.Account, attrs.Direction)) {
accountATs = append(accountATs, &AccountActionTiming{Uuid: at.Uuid, ActionPlanId: at.Id, ActionsId: at.ActionsId, NextExecTime: at.GetNextStartTime(time.Now())})
}
}
}
*reply = accountATs
return nil
}
type AttrRemActionTiming struct {
ActionPlanId string // Id identifying the ActionTimings profile
ActionTimingId string // Internal CGR id identifying particular ActionTiming, *all for all user related ActionTimings to be canceled
Tenant string // Tenant he account belongs to
Account string // Account name
Direction string // Traffic direction
ReloadScheduler bool // If set it will reload the scheduler after adding
}
// Removes an ActionTimings or parts of it depending on filters being set
func (self *ApierV1) RemActionTiming(attrs AttrRemActionTiming, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"ActionPlanId"}); len(missing) != 0 { // Only mandatory ActionPlanId
return utils.NewErrMandatoryIeMissing(missing...)
}
if len(attrs.Account) != 0 { // Presence of Account requires complete account details to be provided
if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "Account", "Direction"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
}
_, err := engine.Guardian.Guard(func() (interface{}, error) {
ats, err := self.RatingDb.GetActionPlans(attrs.ActionPlanId)
if err != nil {
return 0, err
} else if len(ats) == 0 {
return 0, utils.ErrNotFound
}
ats = engine.RemActionPlan(ats, attrs.ActionTimingId, utils.AccountKey(attrs.Tenant, attrs.Account, attrs.Direction))
if err := self.RatingDb.SetActionPlans(attrs.ActionPlanId, ats); err != nil {
return 0, err
}
return 0, nil
}, 0, utils.ACTION_TIMING_PREFIX)
if err != nil {
return utils.NewErrServerError(err)
}
if attrs.ReloadScheduler && self.Sched != nil {
self.Sched.LoadActionPlans(self.RatingDb)
self.Sched.Restart()
}
*reply = OK
return nil
}
// Returns a list of ActionTriggers on an account
func (self *ApierV1) GetAccountActionTriggers(attrs AttrAcntAction, reply *engine.ActionTriggers) error {
if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "Account", "Direction"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
if balance, err := self.AccountDb.GetAccount(utils.AccountKey(attrs.Tenant, attrs.Account, attrs.Direction)); err != nil {
return utils.NewErrServerError(err)
} else {
*reply = balance.ActionTriggers
}
return nil
}
type AttrRemAcntActionTriggers struct {
Tenant string // Tenant he account belongs to
Account string // Account name
Direction string // Traffic direction
ActionTriggersId string // Id filtering only specific id to remove (can be regexp pattern)
}
// Returns a list of ActionTriggers on an account
func (self *ApierV1) RemAccountActionTriggers(attrs AttrRemAcntActionTriggers, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "Account", "Direction"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
balanceId := utils.AccountKey(attrs.Tenant, attrs.Account, attrs.Direction)
_, err := engine.Guardian.Guard(func() (interface{}, error) {
ub, err := self.AccountDb.GetAccount(balanceId)
if err != nil {
return 0, err
}
nactrs := make(engine.ActionTriggers, 0)
for _, actr := range ub.ActionTriggers {
match, _ := regexp.MatchString(attrs.ActionTriggersId, actr.Id)
if len(attrs.ActionTriggersId) != 0 && !match {
nactrs = append(nactrs, actr)
}
}
ub.ActionTriggers = nactrs
if err := self.AccountDb.SetAccount(ub); err != nil {
return 0, err
}
return 0, nil
}, 0, balanceId)
if err != nil {
return utils.NewErrServerError(err)
}
*reply = OK
return nil
}
// Ads a new account into dataDb. If already defined, returns success.
func (self *ApierV1) SetAccount(attr utils.AttrSetAccount, reply *string) error {
if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Direction", "Account"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
balanceId := utils.AccountKey(attr.Tenant, attr.Account, attr.Direction)
var ub *engine.Account
var ats engine.ActionPlans
_, err := engine.Guardian.Guard(func() (interface{}, error) {
if bal, _ := self.AccountDb.GetAccount(balanceId); bal != nil {
ub = bal
} else { // Not found in db, create it here
ub = &engine.Account{
Id: balanceId,
}
}
if len(attr.ActionPlanId) != 0 {
var err error
ats, err = self.RatingDb.GetActionPlans(attr.ActionPlanId)
if err != nil {
return 0, err
}
for _, at := range ats {
at.AccountIds = append(at.AccountIds, balanceId)
}
}
if len(attr.ActionTriggersId) != 0 {
atrs, err := self.RatingDb.GetActionTriggers(attr.ActionTriggersId)
if err != nil {
return 0, err
}
ub.ActionTriggers = atrs
}
if attr.AllowNegative != nil {
ub.AllowNegative = *attr.AllowNegative
}
if attr.Disabled != nil {
ub.Disabled = *attr.Disabled
}
// All prepared, save account
if err := self.AccountDb.SetAccount(ub); err != nil {
return 0, err
}
return 0, nil
}, 0, balanceId)
if err != nil {
return utils.NewErrServerError(err)
}
if len(ats) != 0 {
_, err := engine.Guardian.Guard(func() (interface{}, error) { // ToDo: Try locking it above on read somehow
if err := self.RatingDb.SetActionPlans(attr.ActionPlanId, ats); err != nil {
return 0, err
}
return 0, nil
}, 0, utils.ACTION_TIMING_PREFIX)
if err != nil {
return utils.NewErrServerError(err)
}
if self.Sched != nil {
self.Sched.LoadActionPlans(self.RatingDb)
self.Sched.Restart()
}
}
*reply = OK // This will mark saving of the account, error still can show up in actionTimingsId
return nil
}
func (self *ApierV1) RemoveAccount(attr utils.AttrRemoveAccount, reply *string) error {
if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Direction", "Account"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
accountId := utils.AccountKey(attr.Tenant, attr.Account, attr.Direction)
_, err := engine.Guardian.Guard(func() (interface{}, error) {
if err := self.AccountDb.RemoveAccount(accountId); err != nil {
return 0, err
}
return 0, nil
}, 0, accountId)
if err != nil {
return utils.NewErrServerError(err)
}
*reply = OK
return nil
}
func (self *ApierV1) GetAccounts(attr utils.AttrGetAccounts, reply *[]*engine.Account) error {
if len(attr.Tenant) == 0 {
return utils.NewErrMandatoryIeMissing("Tenanat")
}
if len(attr.Direction) == 0 {
attr.Direction = utils.OUT
}
var accountKeys []string
var err error
if len(attr.AccountIds) == 0 {
if accountKeys, err = self.AccountDb.GetKeysForPrefix(utils.ACCOUNT_PREFIX + utils.ConcatenatedKey(attr.Direction, attr.Tenant)); err != nil {
return err
}
} else {
for _, acntId := range attr.AccountIds {
if len(acntId) == 0 { // Source of error returned from redis (key not found)
continue
}
accountKeys = append(accountKeys, utils.ACCOUNT_PREFIX+utils.ConcatenatedKey(attr.Direction, attr.Tenant, acntId))
}
}
if len(accountKeys) == 0 {
return nil
}
var limitedAccounts []string
if attr.Limit != 0 {
max := math.Min(float64(attr.Offset+attr.Limit), float64(len(accountKeys)))
limitedAccounts = accountKeys[attr.Offset:int(max)]
} else {
limitedAccounts = accountKeys[attr.Offset:]
}
retAccounts := make([]*engine.Account, 0)
for _, acntKey := range limitedAccounts {
if acnt, err := self.AccountDb.GetAccount(acntKey[len(utils.ACCOUNT_PREFIX):]); err != nil && err != utils.ErrNotFound { // Not found is not an error here
return err
} else if acnt != nil {
retAccounts = append(retAccounts, acnt)
}
}
*reply = retAccounts
return nil
}

106
apier/v1/accounts_test.go Normal file
View File

@@ -0,0 +1,106 @@
/*
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 v1
import (
"testing"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
var (
apierAcnts *ApierV1
apierAcntsAcntStorage *engine.MapStorage
)
func init() {
apierAcntsAcntStorage, _ = engine.NewMapStorage()
cfg, _ := config.NewDefaultCGRConfig()
apierAcnts = &ApierV1{AccountDb: engine.AccountingStorage(apierAcntsAcntStorage), Config: cfg}
}
func TestSetAccounts(t *testing.T) {
cgrTenant := "cgrates.org"
iscTenant := "itsyscom.com"
b10 := &engine.Balance{Value: 10, Weight: 10}
cgrAcnt1 := &engine.Account{Id: utils.ConcatenatedKey(utils.OUT, cgrTenant, "account1"),
BalanceMap: map[string]engine.BalanceChain{utils.MONETARY + engine.OUTBOUND: engine.BalanceChain{b10}}}
cgrAcnt2 := &engine.Account{Id: utils.ConcatenatedKey(utils.OUT, cgrTenant, "account2"),
BalanceMap: map[string]engine.BalanceChain{utils.MONETARY + engine.OUTBOUND: engine.BalanceChain{b10}}}
cgrAcnt3 := &engine.Account{Id: utils.ConcatenatedKey(utils.OUT, cgrTenant, "account3"),
BalanceMap: map[string]engine.BalanceChain{utils.MONETARY + engine.OUTBOUND: engine.BalanceChain{b10}}}
iscAcnt1 := &engine.Account{Id: utils.ConcatenatedKey(utils.OUT, iscTenant, "account1"),
BalanceMap: map[string]engine.BalanceChain{utils.MONETARY + engine.OUTBOUND: engine.BalanceChain{b10}}}
iscAcnt2 := &engine.Account{Id: utils.ConcatenatedKey(utils.OUT, iscTenant, "account2"),
BalanceMap: map[string]engine.BalanceChain{utils.MONETARY + engine.OUTBOUND: engine.BalanceChain{b10}}}
for _, account := range []*engine.Account{cgrAcnt1, cgrAcnt2, cgrAcnt3, iscAcnt1, iscAcnt2} {
if err := apierAcntsAcntStorage.SetAccount(account); err != nil {
t.Error(err)
}
}
apierAcntsAcntStorage.CacheRatingPrefixes(utils.ACTION_PREFIX)
}
/*
func TestGetAccountIds(t *testing.T) {
var accountIds []string
var attrs AttrGetAccountIds
if err := apierAcnts.GetAccountIds(attrs, &accountIds); err != nil {
t.Error("Unexpected error", err.Error())
} else if len(accountIds) != 5 {
t.Errorf("Accounts returned: %+v", accountIds)
}
}
*/
func TestGetAccounts(t *testing.T) {
var accounts []*engine.Account
var attrs utils.AttrGetAccounts
if err := apierAcnts.GetAccounts(utils.AttrGetAccounts{Tenant: "cgrates.org"}, &accounts); err != nil {
t.Error("Unexpected error", err.Error())
} else if len(accounts) != 3 {
t.Errorf("Accounts returned: %+v", accounts)
}
attrs = utils.AttrGetAccounts{Tenant: "itsyscom.com"}
if err := apierAcnts.GetAccounts(attrs, &accounts); err != nil {
t.Error("Unexpected error", err.Error())
} else if len(accounts) != 2 {
t.Errorf("Accounts returned: %+v", accounts)
}
attrs = utils.AttrGetAccounts{Tenant: "cgrates.org", AccountIds: []string{"account1"}}
if err := apierAcnts.GetAccounts(attrs, &accounts); err != nil {
t.Error("Unexpected error", err.Error())
} else if len(accounts) != 1 {
t.Errorf("Accounts returned: %+v", accounts)
}
attrs = utils.AttrGetAccounts{Tenant: "itsyscom.com", AccountIds: []string{"INVALID"}}
if err := apierAcnts.GetAccounts(attrs, &accounts); err != nil {
t.Error("Unexpected error", err.Error())
} else if len(accounts) != 0 {
t.Errorf("Accounts returned: %+v", accounts)
}
attrs = utils.AttrGetAccounts{Tenant: "INVALID"}
if err := apierAcnts.GetAccounts(attrs, &accounts); err != nil {
t.Error("Unexpected error", err.Error())
} else if len(accounts) != 0 {
t.Errorf("Accounts returned: %+v", accounts)
}
}

140
apier/v1/aliases.go Normal file
View File

@@ -0,0 +1,140 @@
/*
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 v1
import (
"errors"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
type AttrAddRatingSubjectAliases struct {
Tenant, Category, Subject string
Aliases []string
}
type AttrAddAccountAliases struct {
Tenant, Category, Account string
Aliases []string
}
// Add aliases configured for a rating profile subject <Deprecated>
func (self *ApierV1) AddRatingSubjectAliases(attrs AttrAddRatingSubjectAliases, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "Subject", "Aliases"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
if attrs.Category == "" {
attrs.Category = utils.CALL
}
aliases := engine.GetAliasService()
if aliases == nil {
return errors.New("ALIASES_NOT_ENABLED")
}
var ignr string
for _, alias := range attrs.Aliases {
if err := aliases.SetAlias(
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{"Subject": map[string]string{alias: attrs.Subject}}, Weight: 10.0}}}, &ignr); err != nil {
return utils.NewErrServerError(err)
}
}
*reply = utils.OK
return nil
}
// Remove aliases configured for a rating profile subject
func (self *ApierV1) RemRatingSubjectAliases(tenantRatingSubject engine.TenantRatingSubject, reply *string) error {
if missing := utils.MissingStructFields(&tenantRatingSubject, []string{"Tenant", "Subject"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
aliases := engine.GetAliasService()
if aliases == nil {
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 {
return utils.NewErrServerError(err)
}
var ignr string
for _, aliass := range reverseAliases {
for _, alias := range aliass {
if alias.Tenant != tenantRatingSubject.Tenant {
continue // From another tenant
}
if err := aliases.RemoveAlias(*alias, &ignr); err != nil {
return utils.NewErrServerError(err)
}
}
}
*reply = utils.OK
return nil
}
func (self *ApierV1) AddAccountAliases(attrs AttrAddAccountAliases, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "Account", "Aliases"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
if attrs.Category == "" {
attrs.Category = utils.CALL
}
aliases := engine.GetAliasService()
if aliases == nil {
return errors.New("ALIASES_NOT_ENABLED")
}
var ignr string
for _, alias := range attrs.Aliases {
if err := aliases.SetAlias(
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}}, Weight: 10.0}}}, &ignr); err != nil {
return utils.NewErrServerError(err)
}
}
*reply = utils.OK
return nil
}
// Remove aliases configured for an account
func (self *ApierV1) RemAccountAliases(tenantAccount engine.TenantAccount, reply *string) error {
if missing := utils.MissingStructFields(&tenantAccount, []string{"Tenant", "Account"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
aliases := engine.GetAliasService()
if aliases == nil {
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 {
return utils.NewErrServerError(err)
}
var ignr string
for _, aliass := range reverseAliases {
for _, alias := range aliass {
if alias.Tenant != tenantAccount.Tenant {
continue // From another tenant
}
if err := aliases.RemoveAlias(*alias, &ignr); err != nil {
return utils.NewErrServerError(err)
}
}
}
*reply = utils.OK
return nil
}

1267
apier/v1/apier.go Normal file

File diff suppressed because it is too large Load Diff

1779
apier/v1/apier_local_test.go Normal file

File diff suppressed because it is too large Load Diff

73
apier/v1/auth.go Normal file
View File

@@ -0,0 +1,73 @@
/*
Real-time Charging System 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 v1
import (
"strconv"
"time"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Returns MaxUsage (for calls in seconds), -1 for no limit
func (self *ApierV1) GetMaxUsage(usageRecord engine.UsageRecord, maxUsage *float64) error {
err := engine.LoadUserProfile(&usageRecord, "ExtraFields")
if err != nil {
return err
}
if usageRecord.TOR == "" {
usageRecord.TOR = utils.VOICE
}
if usageRecord.ReqType == "" {
usageRecord.ReqType = self.Config.DefaultReqType
}
if usageRecord.Direction == "" {
usageRecord.Direction = utils.OUT
}
if usageRecord.Tenant == "" {
usageRecord.Tenant = self.Config.DefaultTenant
}
if usageRecord.Category == "" {
usageRecord.Category = self.Config.DefaultCategory
}
if usageRecord.Subject == "" {
usageRecord.Subject = usageRecord.Account
}
if usageRecord.SetupTime == "" {
usageRecord.SetupTime = utils.META_NOW
}
if usageRecord.Usage == "" {
usageRecord.Usage = strconv.FormatFloat(self.Config.MaxCallDuration.Seconds(), 'f', -1, 64)
}
storedCdr, err := usageRecord.AsStoredCdr(self.Config.DefaultTimezone)
if err != nil {
return utils.NewErrServerError(err)
}
var maxDur float64
if err := self.Responder.GetDerivedMaxSessionTime(storedCdr, &maxDur); err != nil {
return err
}
if maxDur == -1.0 {
*maxUsage = -1.0
return nil
}
*maxUsage = time.Duration(maxDur).Seconds()
return nil
}

43
apier/v1/cdrc.go Normal file
View File

@@ -0,0 +1,43 @@
/*
Real-time Charging System for Telecom & ISP environments
Copyright (C) 2012-2015 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 v1
import (
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/utils"
)
type AttrReloadConfig struct {
ConfigDir string
}
// Retrieves the callCost out of CGR logDb
func (apier *ApierV1) ReloadCdrcConfig(attrs AttrReloadConfig, reply *string) error {
if attrs.ConfigDir == "" {
attrs.ConfigDir = utils.CONFIG_DIR
}
newCfg, err := config.NewCGRConfigFromFolder(attrs.ConfigDir)
if err != nil {
return utils.NewErrServerError(err)
}
apier.Config.CdrcProfiles = newCfg.CdrcProfiles // ToDo: Check if there can be any concurency involved here so we need to lock maybe
apier.Config.ConfigReloads[utils.CDRC] <- struct{}{}
*reply = OK
return nil
}

230
apier/v1/cdre.go Normal file
View File

@@ -0,0 +1,230 @@
/*
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 v1
import (
"archive/zip"
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"strconv"
"strings"
"time"
"unicode/utf8"
"github.com/cgrates/cgrates/cdre"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
func (self *ApierV1) ExportCdrsToZipString(attr utils.AttrExpFileCdrs, reply *string) error {
tmpDir := "/tmp"
attr.ExportDir = &tmpDir // Enforce exporting to tmp always so we avoid cleanup issues
efc := utils.ExportedFileCdrs{}
if err := self.ExportCdrsToFile(attr, &efc); err != nil {
return err
} else if efc.TotalRecords == 0 || len(efc.ExportedFilePath) == 0 {
return errors.New("No CDR records to export")
}
// Create a buffer to write our archive to.
buf := new(bytes.Buffer)
// Create a new zip archive.
w := zip.NewWriter(buf)
// read generated file
content, err := ioutil.ReadFile(efc.ExportedFilePath)
if err != nil {
return err
}
exportFileName := path.Base(efc.ExportedFilePath)
f, err := w.Create(exportFileName)
if err != nil {
return err
}
_, err = f.Write(content)
if err != nil {
return err
}
// Write metadata into a separate file with extension .cgr
medaData, err := json.MarshalIndent(efc, "", " ")
if err != nil {
errors.New("Failed creating metadata content")
}
medatadaFileName := exportFileName[:len(path.Ext(exportFileName))] + ".cgr"
mf, err := w.Create(medatadaFileName)
if err != nil {
return err
}
_, err = mf.Write(medaData)
if err != nil {
return err
}
// Make sure to check the error on Close.
if err := w.Close(); err != nil {
return err
}
if err := os.Remove(efc.ExportedFilePath); err != nil {
fmt.Errorf("Failed removing exported file at path: %s", efc.ExportedFilePath)
}
*reply = base64.StdEncoding.EncodeToString(buf.Bytes())
return nil
}
// Export Cdrs to file
func (self *ApierV1) ExportCdrsToFile(attr utils.AttrExpFileCdrs, reply *utils.ExportedFileCdrs) error {
var err error
cdreReloadStruct := <-self.Config.ConfigReloads[utils.CDRE] // Read the content of the channel, locking it
defer func() { self.Config.ConfigReloads[utils.CDRE] <- cdreReloadStruct }() // Unlock reloads at exit
exportTemplate := self.Config.CdreProfiles[utils.META_DEFAULT]
if attr.ExportTemplate != nil && len(*attr.ExportTemplate) != 0 { // Export template prefered, use it
var hasIt bool
if exportTemplate, hasIt = self.Config.CdreProfiles[*attr.ExportTemplate]; !hasIt {
return fmt.Errorf("%s:ExportTemplate", utils.ErrNotFound.Error())
}
}
if exportTemplate == nil {
return fmt.Errorf("%s:ExportTemplate", utils.ErrMandatoryIeMissing.Error())
}
cdrFormat := exportTemplate.CdrFormat
if attr.CdrFormat != nil && len(*attr.CdrFormat) != 0 {
cdrFormat = strings.ToLower(*attr.CdrFormat)
}
if !utils.IsSliceMember(utils.CdreCdrFormats, cdrFormat) {
return fmt.Errorf("%s:%s", utils.ErrMandatoryIeMissing.Error(), "CdrFormat")
}
fieldSep := exportTemplate.FieldSeparator
if attr.FieldSeparator != nil && len(*attr.FieldSeparator) != 0 {
fieldSep, _ = utf8.DecodeRuneInString(*attr.FieldSeparator)
if fieldSep == utf8.RuneError {
return fmt.Errorf("%s:FieldSeparator:%s", utils.ErrServerError.Error(), "Invalid")
}
}
exportDir := exportTemplate.ExportDir
if attr.ExportDir != nil && len(*attr.ExportDir) != 0 {
exportDir = *attr.ExportDir
}
exportId := strconv.FormatInt(time.Now().Unix(), 10)
if attr.ExportId != nil && len(*attr.ExportId) != 0 {
exportId = *attr.ExportId
}
fileName := fmt.Sprintf("cdre_%s.%s", exportId, cdrFormat)
if attr.ExportFileName != nil && len(*attr.ExportFileName) != 0 {
fileName = *attr.ExportFileName
}
filePath := path.Join(exportDir, fileName)
if cdrFormat == utils.DRYRUN {
filePath = utils.DRYRUN
}
dataUsageMultiplyFactor := exportTemplate.DataUsageMultiplyFactor
if attr.DataUsageMultiplyFactor != nil && *attr.DataUsageMultiplyFactor != 0.0 {
dataUsageMultiplyFactor = *attr.DataUsageMultiplyFactor
}
smsUsageMultiplyFactor := exportTemplate.SmsUsageMultiplyFactor
if attr.SmsUsageMultiplyFactor != nil && *attr.SmsUsageMultiplyFactor != 0.0 {
smsUsageMultiplyFactor = *attr.SmsUsageMultiplyFactor
}
genericUsageMultiplyFactor := exportTemplate.GenericUsageMultiplyFactor
if attr.GenericUsageMultiplyFactor != nil && *attr.GenericUsageMultiplyFactor != 0.0 {
genericUsageMultiplyFactor = *attr.GenericUsageMultiplyFactor
}
costMultiplyFactor := exportTemplate.CostMultiplyFactor
if attr.CostMultiplyFactor != nil && *attr.CostMultiplyFactor != 0.0 {
costMultiplyFactor = *attr.CostMultiplyFactor
}
costShiftDigits := exportTemplate.CostShiftDigits
if attr.CostShiftDigits != nil {
costShiftDigits = *attr.CostShiftDigits
}
roundingDecimals := exportTemplate.CostRoundingDecimals
if attr.RoundDecimals != nil {
roundingDecimals = *attr.RoundDecimals
}
maskDestId := exportTemplate.MaskDestId
if attr.MaskDestinationId != nil && len(*attr.MaskDestinationId) != 0 {
maskDestId = *attr.MaskDestinationId
}
maskLen := exportTemplate.MaskLength
if attr.MaskLength != nil {
maskLen = *attr.MaskLength
}
cdrsFltr, err := attr.AsCdrsFilter(self.Config.DefaultTimezone)
if err != nil {
return utils.NewErrServerError(err)
}
cdrs, _, err := self.CdrDb.GetStoredCdrs(cdrsFltr)
if err != nil {
return err
} else if len(cdrs) == 0 {
*reply = utils.ExportedFileCdrs{ExportedFilePath: ""}
return nil
}
cdrexp, err := cdre.NewCdrExporter(cdrs, self.CdrDb, exportTemplate, cdrFormat, fieldSep, exportId, dataUsageMultiplyFactor, smsUsageMultiplyFactor, genericUsageMultiplyFactor,
costMultiplyFactor, costShiftDigits, roundingDecimals, self.Config.RoundingDecimals, maskDestId, maskLen, self.Config.HttpSkipTlsVerify, self.Config.DefaultTimezone)
if err != nil {
return utils.NewErrServerError(err)
}
if cdrexp.TotalExportedCdrs() == 0 {
*reply = utils.ExportedFileCdrs{ExportedFilePath: ""}
return nil
}
if err := cdrexp.WriteToFile(filePath); err != nil {
return utils.NewErrServerError(err)
}
*reply = utils.ExportedFileCdrs{ExportedFilePath: filePath, TotalRecords: len(cdrs), TotalCost: cdrexp.TotalCost(), FirstOrderId: cdrexp.FirstOrderId(), LastOrderId: cdrexp.LastOrderId()}
if !attr.SuppressCgrIds {
reply.ExportedCgrIds = cdrexp.PositiveExports()
reply.UnexportedCgrIds = cdrexp.NegativeExports()
}
return nil
}
// Remove Cdrs out of CDR storage
func (self *ApierV1) RemCdrs(attrs utils.AttrRemCdrs, reply *string) error {
if len(attrs.CgrIds) == 0 {
return fmt.Errorf("%s:CgrIds", utils.ErrMandatoryIeMissing.Error())
}
if err := self.CdrDb.RemStoredCdrs(attrs.CgrIds); err != nil {
return utils.NewErrServerError(err)
}
*reply = "OK"
return nil
}
// Reloads CDRE configuration out of folder specified
func (apier *ApierV1) ReloadCdreConfig(attrs AttrReloadConfig, reply *string) error {
if attrs.ConfigDir == "" {
attrs.ConfigDir = utils.CONFIG_DIR
}
newCfg, err := config.NewCGRConfigFromFolder(attrs.ConfigDir)
if err != nil {
return utils.NewErrServerError(err)
}
cdreReloadStruct := <-apier.Config.ConfigReloads[utils.CDRE] // Get the CDRE reload channel // Read the content of the channel, locking it
apier.Config.CdreProfiles = newCfg.CdreProfiles
apier.Config.ConfigReloads[utils.CDRE] <- cdreReloadStruct // Unlock reloads
engine.Logger.Info("<CDRE> Configuration reloaded")
*reply = OK
return nil
}

60
apier/v1/cdrs.go Normal file
View File

@@ -0,0 +1,60 @@
/*
Real-time Charging System for Telecom & ISP environments
Copyright (C) 2012-2015 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 v1
import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Retrieves the callCost out of CGR logDb
func (apier *ApierV1) GetCallCostLog(attrs utils.AttrGetCallCost, reply *engine.CallCost) 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 {
return utils.NewErrServerError(err)
} else if cc == nil {
return utils.ErrNotFound
} else {
*reply = *cc
}
return nil
}
// Retrieves CDRs based on the filters
func (apier *ApierV1) GetCdrs(attrs utils.AttrGetCdrs, reply *[]*engine.ExternalCdr) error {
cdrsFltr, err := attrs.AsCdrsFilter(apier.Config.DefaultTimezone)
if err != nil {
return utils.NewErrServerError(err)
}
if cdrs, _, err := apier.CdrDb.GetStoredCdrs(cdrsFltr); err != nil {
return utils.NewErrServerError(err)
} else if len(cdrs) == 0 {
*reply = make([]*engine.ExternalCdr, 0)
} else {
for _, cdr := range cdrs {
*reply = append(*reply, cdr.AsExternalCdr())
}
}
return nil
}

70
apier/v1/cdrstatsv1.go Normal file
View File

@@ -0,0 +1,70 @@
/*
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 v1
import (
"fmt"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Interact with Stats server
type CDRStatsV1 struct {
CdrStats engine.StatsInterface
}
type AttrGetMetrics struct {
StatsQueueId string // Id of the stats instance queried
}
func (sts *CDRStatsV1) GetMetrics(attr AttrGetMetrics, reply *map[string]float64) error {
if len(attr.StatsQueueId) == 0 {
return fmt.Errorf("%s:StatsQueueId", utils.ErrMandatoryIeMissing.Error())
}
return sts.CdrStats.GetValues(attr.StatsQueueId, reply)
}
func (sts *CDRStatsV1) GetQueueIds(empty string, reply *[]string) error {
return sts.CdrStats.GetQueueIds(0, reply)
}
func (sts *CDRStatsV1) GetQueue(id string, sq *engine.StatsQueue) error {
return sts.CdrStats.GetQueue(id, sq)
}
func (sts *CDRStatsV1) GetQueueTriggers(id string, ats *engine.ActionTriggers) error {
return sts.CdrStats.GetQueueTriggers(id, ats)
}
func (sts *CDRStatsV1) ReloadQueues(attr utils.AttrCDRStatsReloadQueues, reply *string) error {
if err := sts.CdrStats.ReloadQueues(attr.StatsQueueIds, nil); err != nil {
return err
}
*reply = utils.OK
return nil
}
func (sts *CDRStatsV1) ResetQueues(attr utils.AttrCDRStatsReloadQueues, reply *string) error {
if err := sts.CdrStats.ResetQueues(attr.StatsQueueIds, nil); err != nil {
return err
}
*reply = utils.OK
return nil
}

View File

@@ -0,0 +1,204 @@
/*
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 v1
import (
"fmt"
"net/http"
"net/rpc"
"net/rpc/jsonrpc"
"path"
"reflect"
"testing"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
var cdrstCfgPath string
var cdrstCfg *config.CGRConfig
var cdrstRpc *rpc.Client
func TestCDRStatsLclLoadConfig(t *testing.T) {
if !*testLocal {
return
}
var err error
cdrstCfgPath = path.Join(*dataDir, "conf", "samples", "cdrstats")
if cdrstCfg, err = config.NewCGRConfigFromFolder(cfgPath); err != nil {
t.Error(err)
}
}
func TestCDRStatsLclInitDataDb(t *testing.T) {
if !*testLocal {
return
}
if err := engine.InitDataDb(cdrstCfg); err != nil {
t.Fatal(err)
}
}
func TestCDRStatsLclStartEngine(t *testing.T) {
if !*testLocal {
return
}
if _, err := engine.StopStartEngine(cdrstCfgPath, 1000); err != nil {
t.Fatal(err)
}
}
// Connect rpc client to rater
func TestCDRStatsLclRpcConn(t *testing.T) {
if !*testLocal {
return
}
var err error
cdrstRpc, err = jsonrpc.Dial("tcp", cdrstCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
if err != nil {
t.Fatal("Could not connect to rater: ", err.Error())
}
}
func TestCDRStatsLclLoadTariffPlanFromFolder(t *testing.T) {
if !*testLocal {
return
}
reply := ""
// Simple test that command is executed without errors
attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "cdrstats")}
if err := cdrstRpc.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil {
t.Error("Got error on ApierV1.LoadTariffPlanFromFolder: ", err.Error())
} else if reply != "OK" {
t.Error("Calling ApierV1.LoadTariffPlanFromFolder got reply: ", reply)
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups
}
func TestCDRStatsLclGetQueueIds2(t *testing.T) {
if !*testLocal {
return
}
var queueIds []string
eQueueIds := []string{"CDRST3", "CDRST4"}
if err := cdrstRpc.Call("CDRStatsV1.GetQueueIds", "", &queueIds); err != nil {
t.Error("Calling CDRStatsV1.GetQueueIds, got error: ", err.Error())
} else if len(eQueueIds) != len(queueIds) {
t.Errorf("Expecting: %v, received: %v", eQueueIds, queueIds)
}
}
func TestCDRStatsLclPostCdrs(t *testing.T) {
if !*testLocal {
return
}
httpClient := new(http.Client)
storedCdrs := []*engine.StoredCdr{
&engine.StoredCdr{CgrId: utils.Sha1("dsafdsafa", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "test",
ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1001", Subject: "1001", Destination: "+4986517174963", SetupTime: time.Now(),
AnswerTime: time.Now(), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
Cost: 1.01, RatedAccount: "dan", RatedSubject: "dan",
},
&engine.StoredCdr{CgrId: utils.Sha1("dsafdsafb", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "test",
ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1001", Subject: "1001", Destination: "+4986517174963", SetupTime: time.Now(),
AnswerTime: time.Now(), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(5) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dan",
},
&engine.StoredCdr{CgrId: utils.Sha1("dsafdsafc", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "test",
ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1001", Subject: "1001", Destination: "+4986517174963", SetupTime: time.Now(), AnswerTime: time.Now(),
MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(30) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dan",
},
&engine.StoredCdr{CgrId: utils.Sha1("dsafdsafd", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "test",
ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1001", Subject: "1001", Destination: "+4986517174963", SetupTime: time.Now(), AnswerTime: time.Time{},
MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(0) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dan",
},
}
for _, storedCdr := range storedCdrs {
if _, err := httpClient.PostForm(fmt.Sprintf("http://%s/cdr_http", "127.0.0.1:2080"), storedCdr.AsHttpForm()); err != nil {
t.Error(err.Error())
}
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond)
}
func TestCDRStatsLclGetMetrics1(t *testing.T) {
if !*testLocal {
return
}
var rcvMetrics2 map[string]float64
expectedMetrics2 := map[string]float64{"ASR": 75, "ACD": 15}
if err := cdrstRpc.Call("CDRStatsV1.GetMetrics", AttrGetMetrics{StatsQueueId: "CDRST4"}, &rcvMetrics2); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(expectedMetrics2, rcvMetrics2) {
t.Errorf("Expecting: %v, received: %v", expectedMetrics2, rcvMetrics2)
}
}
// Test stats persistence
func TestCDRStatsLclStatsPersistence(t *testing.T) {
if !*testLocal {
return
}
time.Sleep(time.Duration(2) * time.Second) // Allow stats to be updated in dataDb
if _, err := engine.StopStartEngine(cdrstCfgPath, *waitRater); err != nil {
t.Fatal(err)
}
var err error
cdrstRpc, err = jsonrpc.Dial("tcp", cdrstCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
if err != nil {
t.Fatal("Could not connect to rater: ", err.Error())
}
var rcvMetrics map[string]float64
expectedMetrics := map[string]float64{"ASR": 75, "ACD": 15}
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 TestCDRStatsLclResetMetrics(t *testing.T) {
if !*testLocal {
return
}
var reply string
if err := cdrstRpc.Call("CDRStatsV1.ResetQueues", utils.AttrCDRStatsReloadQueues{StatsQueueIds: []string{"CDRST4"}}, &reply); err != nil {
t.Error("Calling CDRStatsV1.ResetQueues, got error: ", err.Error())
} else if reply != utils.OK {
t.Error("Unexpected reply received: ", reply)
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond)
var rcvMetrics2 map[string]float64
expectedMetrics2 := map[string]float64{"ASR": -1, "ACD": -1}
if err := cdrstRpc.Call("CDRStatsV1.GetMetrics", AttrGetMetrics{StatsQueueId: "CDRST4"}, &rcvMetrics2); err != nil {
t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error())
} else if !reflect.DeepEqual(expectedMetrics2, rcvMetrics2) {
t.Errorf("Expecting: %v, received: %v", expectedMetrics2, rcvMetrics2)
}
}

82
apier/v1/cdrsv1.go Normal file
View File

@@ -0,0 +1,82 @@
/*
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 v1
import (
"time"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Receive CDRs via RPC methods
type CdrsV1 struct {
CdrSrv *engine.CdrServer
}
// Designed for CGR internal usage
func (self *CdrsV1) ProcessCdr(cdr *engine.StoredCdr, reply *string) error {
if err := self.CdrSrv.ProcessCdr(cdr); err != nil {
return utils.NewErrServerError(err)
}
*reply = utils.OK
return nil
}
// Designed for external programs feeding CDRs to CGRateS
func (self *CdrsV1) ProcessExternalCdr(cdr *engine.ExternalCdr, reply *string) error {
if err := self.CdrSrv.ProcessExternalCdr(cdr); err != nil {
return utils.NewErrServerError(err)
}
*reply = utils.OK
return nil
}
// Remotely start mediation with specific runid, runs asynchronously, it's status will be displayed in syslog
func (self *CdrsV1) RateCdrs(attrs utils.AttrRateCdrs, reply *string) error {
var tStart, tEnd time.Time
var err error
if len(attrs.TimeStart) != 0 {
if tStart, err = utils.ParseTimeDetectLayout(attrs.TimeStart, self.CdrSrv.Timezone()); err != nil {
return err
}
}
if len(attrs.TimeEnd) != 0 {
if tEnd, err = utils.ParseTimeDetectLayout(attrs.TimeEnd, self.CdrSrv.Timezone()); err != nil {
return err
}
}
//RateCdrs(cgrIds, runIds, tors, cdrHosts, cdrSources, reqTypes, directions, tenants, categories, accounts, subjects, destPrefixes []string,
//orderIdStart, orderIdEnd int64, timeStart, timeEnd time.Time, rerateErrors, rerateRated bool)
if err := self.CdrSrv.RateCdrs(attrs.CgrIds, attrs.MediationRunIds, attrs.TORs, attrs.CdrHosts, attrs.CdrSources, attrs.ReqTypes, attrs.Directions,
attrs.Tenants, attrs.Categories, attrs.Accounts, attrs.Subjects, attrs.DestinationPrefixes, attrs.RatedAccounts, attrs.RatedSubjects,
attrs.OrderIdStart, attrs.OrderIdEnd, tStart, tEnd, attrs.RerateErrors, attrs.RerateRated, attrs.SendToStats); err != nil {
return utils.NewErrServerError(err)
}
*reply = utils.OK
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
}

59
apier/v1/costs.go Normal file
View File

@@ -0,0 +1,59 @@
/*
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 v1
import (
"time"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
type AttrGetDataCost struct {
Direction string
Category string
Tenant, Account, Subject string
StartTime time.Time
Usage int64 // the call duration so far (till TimeEnd)
}
func (apier *ApierV1) GetDataCost(attrs AttrGetDataCost, reply *engine.DataCost) error {
usageAsDuration := time.Duration(attrs.Usage) * time.Second // Convert to seconds to match the loaded rates
cd := &engine.CallDescriptor{
Direction: attrs.Direction,
Category: attrs.Category,
Tenant: attrs.Tenant,
Account: attrs.Account,
Subject: attrs.Subject,
TimeStart: attrs.StartTime,
TimeEnd: attrs.StartTime.Add(usageAsDuration),
DurationIndex: usageAsDuration,
TOR: utils.DATA,
}
var cc engine.CallCost
if err := apier.Responder.GetCost(cd, &cc); err != nil {
return utils.NewErrServerError(err)
}
if dc, err := cc.ToDataCost(); err != nil {
return utils.NewErrServerError(err)
} else if dc != nil {
*reply = *dc
}
return nil
}

66
apier/v1/debit.go Normal file
View File

@@ -0,0 +1,66 @@
/*
Real-time Charging System 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 v1
import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
func (self *ApierV1) DebitUsage(usageRecord engine.UsageRecord, reply *string) error {
if missing := utils.MissingStructFields(&usageRecord, []string{"Account", "Destination", "Usage"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
err := engine.LoadUserProfile(usageRecord, "")
if err != nil {
*reply = err.Error()
return err
}
if usageRecord.TOR == "" {
usageRecord.TOR = utils.VOICE
}
if usageRecord.ReqType == "" {
usageRecord.ReqType = self.Config.DefaultReqType
}
if usageRecord.Direction == "" {
usageRecord.Direction = utils.OUT
}
if usageRecord.Tenant == "" {
usageRecord.Tenant = self.Config.DefaultTenant
}
if usageRecord.Category == "" {
usageRecord.Category = self.Config.DefaultCategory
}
if usageRecord.Subject == "" {
usageRecord.Subject = usageRecord.Account
}
if usageRecord.AnswerTime == "" {
usageRecord.AnswerTime = utils.META_NOW
}
cd, err := usageRecord.AsCallDescriptor(self.Config.DefaultTimezone)
if err != nil {
return utils.NewErrServerError(err)
}
var cc engine.CallCost
if err := self.Responder.Debit(cd, &cc); err != nil {
return utils.NewErrServerError(err)
}
*reply = OK
return nil
}

117
apier/v1/derivedcharging.go Normal file
View File

@@ -0,0 +1,117 @@
/*
Real-time Charging System for Telecom & ISP environments
Copyright (C) 2012-2015 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 v1
import (
"fmt"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Get DerivedChargers applying to our call, appends general configured to account specific ones if that is configured
func (self *ApierV1) GetDerivedChargers(attrs utils.AttrDerivedChargers, reply *utils.DerivedChargers) (err error) {
if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "Direction", "Account", "Subject"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
if hDc, err := engine.HandleGetDerivedChargers(self.RatingDb, &attrs); err != nil {
return utils.NewErrServerError(err)
} else if hDc != nil {
*reply = hDc
}
return nil
}
type AttrSetDerivedChargers struct {
Direction, Tenant, Category, Account, Subject string
DerivedChargers utils.DerivedChargers
Overwrite bool // Do not overwrite if present in redis
}
func (self *ApierV1) SetDerivedChargers(attrs AttrSetDerivedChargers, reply *string) (err error) {
if len(attrs.Direction) == 0 {
attrs.Direction = utils.OUT
}
if len(attrs.Tenant) == 0 {
attrs.Tenant = utils.ANY
}
if len(attrs.Category) == 0 {
attrs.Category = utils.ANY
}
if len(attrs.Account) == 0 {
attrs.Account = utils.ANY
}
if len(attrs.Subject) == 0 {
attrs.Subject = utils.ANY
}
for _, dc := range attrs.DerivedChargers {
if _, err = utils.ParseRSRFields(dc.RunFilters, utils.INFIELD_SEP); err != nil { // Make sure rules are OK before loading in db
return fmt.Errorf("%s:%s", utils.ErrParserError.Error(), err.Error())
}
}
dcKey := utils.DerivedChargersKey(attrs.Direction, attrs.Tenant, attrs.Category, attrs.Account, attrs.Subject)
if !attrs.Overwrite {
if exists, err := self.RatingDb.HasData(utils.DESTINATION_PREFIX, dcKey); err != nil {
return utils.NewErrServerError(err)
} else if exists {
return utils.ErrExists
}
}
if err := self.RatingDb.SetDerivedChargers(dcKey, attrs.DerivedChargers); err != nil {
return utils.NewErrServerError(err)
}
if err := self.RatingDb.CacheRatingPrefixValues(map[string][]string{
utils.DERIVEDCHARGERS_PREFIX: []string{utils.DERIVEDCHARGERS_PREFIX + dcKey},
}); err != nil {
return utils.NewErrServerError(err)
}
*reply = utils.OK
return nil
}
type AttrRemDerivedChargers struct {
Direction, Tenant, Category, Account, Subject string
}
func (self *ApierV1) RemDerivedChargers(attrs AttrRemDerivedChargers, reply *string) error {
if len(attrs.Direction) == 0 {
attrs.Direction = utils.OUT
}
if len(attrs.Tenant) == 0 {
attrs.Tenant = utils.ANY
}
if len(attrs.Category) == 0 {
attrs.Category = utils.ANY
}
if len(attrs.Account) == 0 {
attrs.Account = utils.ANY
}
if len(attrs.Subject) == 0 {
attrs.Subject = utils.ANY
}
if err := self.RatingDb.SetDerivedChargers(utils.DerivedChargersKey(attrs.Direction, attrs.Tenant, attrs.Category, attrs.Account, attrs.Subject), nil); err != nil {
return utils.NewErrServerError(err)
} else {
*reply = "OK"
}
if err := self.RatingDb.CacheRatingPrefixes(utils.DERIVEDCHARGERS_PREFIX); err != nil {
return utils.NewErrServerError(err)
}
return nil
}

View File

@@ -0,0 +1,105 @@
/*
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 v1
import (
"reflect"
"testing"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
var apierDcT *ApierV1
func init() {
dataStorage, _ := engine.NewMapStorage()
cfg, _ := config.NewDefaultCGRConfig()
apierDcT = &ApierV1{RatingDb: engine.RatingStorage(dataStorage), Config: cfg}
}
/*
func TestGetEmptyDC(t *testing.T) {
attrs := utils.AttrDerivedChargers{Tenant: "cgrates.org", Category: "call", Direction: "*out", Account: "dan", Subject: "dan"}
var dcs utils.DerivedChargers
if err := apierDcT.GetDerivedChargers(attrs, &dcs); err != nil {
t.Error("Unexpected error", err.Error())
} else if !reflect.DeepEqual(dcs, apierDcT.Config.DerivedChargers) {
t.Error("Returned DerivedChargers not matching the configured ones")
}
}
*/
func TestSetDC(t *testing.T) {
dcs1 := utils.DerivedChargers{
&utils.DerivedCharger{RunId: "extra1", ReqTypeField: "^prepaid", DirectionField: "*default", TenantField: "*default", CategoryField: "*default",
AccountField: "rif", SubjectField: "rif", DestinationField: "*default", SetupTimeField: "*default", AnswerTimeField: "*default", UsageField: "*default"},
&utils.DerivedCharger{RunId: "extra2", ReqTypeField: "*default", DirectionField: "*default", TenantField: "*default", CategoryField: "*default",
AccountField: "ivo", SubjectField: "ivo", DestinationField: "*default", SetupTimeField: "*default", AnswerTimeField: "*default", UsageField: "*default"},
}
attrs := AttrSetDerivedChargers{Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "dan", Subject: "dan", DerivedChargers: dcs1}
var reply string
if err := apierDcT.SetDerivedChargers(attrs, &reply); err != nil {
t.Error("Unexpected error", err.Error())
} else if reply != utils.OK {
t.Error("Unexpected reply returned", reply)
}
}
func TestGetDC(t *testing.T) {
attrs := utils.AttrDerivedChargers{Tenant: "cgrates.org", Category: "call", Direction: "*out", Account: "dan", Subject: "dan"}
eDcs := utils.DerivedChargers{
&utils.DerivedCharger{RunId: "extra1", ReqTypeField: "^prepaid", DirectionField: "*default", TenantField: "*default", CategoryField: "*default",
AccountField: "rif", SubjectField: "rif", DestinationField: "*default", SetupTimeField: "*default", AnswerTimeField: "*default", UsageField: "*default"},
&utils.DerivedCharger{RunId: "extra2", ReqTypeField: "*default", DirectionField: "*default", TenantField: "*default", CategoryField: "*default",
AccountField: "ivo", SubjectField: "ivo", DestinationField: "*default", SetupTimeField: "*default", AnswerTimeField: "*default", UsageField: "*default"},
}
var dcs utils.DerivedChargers
if err := apierDcT.GetDerivedChargers(attrs, &dcs); err != nil {
t.Error("Unexpected error", err.Error())
} else if !reflect.DeepEqual(dcs, eDcs) {
t.Errorf("Expecting: %v, received: %v", eDcs, dcs)
}
}
func TestRemDC(t *testing.T) {
attrs := AttrRemDerivedChargers{Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "dan", Subject: "dan"}
var reply string
if err := apierDcT.RemDerivedChargers(attrs, &reply); err != nil {
t.Error("Unexpected error", err.Error())
} else if reply != utils.OK {
t.Error("Unexpected reply returned", reply)
}
}
/*
func TestGetEmptyDC2(t *testing.T) {
attrs := utils.AttrDerivedChargers{Tenant: "cgrates.org", Category: "call", Direction: "*out", Account: "dan", Subject: "dan"}
var dcs utils.DerivedChargers
if err := apierDcT.GetDerivedChargers(attrs, &dcs); err != nil {
t.Error("Unexpected error", err.Error())
} else if !reflect.DeepEqual(dcs, apierDcT.Config.DerivedChargers) {
for _, dc := range dcs {
fmt.Printf("Got dc: %v\n", dc)
}
t.Error("Returned DerivedChargers not matching the configured ones")
}
}
*/

83
apier/v1/lcr.go Normal file
View File

@@ -0,0 +1,83 @@
/*
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 v1
import (
"fmt"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Computes the LCR for a specific request emulating a call
func (self *ApierV1) GetLcr(lcrReq engine.LcrRequest, lcrReply *engine.LcrReply) error {
cd, err := lcrReq.AsCallDescriptor(self.Config.DefaultTimezone)
if err != nil {
return err
}
var lcrQried engine.LCRCost
if err := self.Responder.GetLCR(&engine.AttrGetLcr{CallDescriptor: cd, Paginator: lcrReq.Paginator}, &lcrQried); err != nil {
return utils.NewErrServerError(err)
}
if lcrQried.Entry == nil {
return utils.ErrNotFound
}
lcrReply.DestinationId = lcrQried.Entry.DestinationId
lcrReply.RPCategory = lcrQried.Entry.RPCategory
lcrReply.Strategy = lcrQried.Entry.Strategy
for _, qriedSuppl := range lcrQried.SupplierCosts {
if qriedSuppl.Error != "" {
engine.Logger.Err(fmt.Sprintf("LCR_ERROR: supplier <%s>, error <%s>", qriedSuppl.Supplier, qriedSuppl.Error))
if !lcrReq.IgnoreErrors {
return fmt.Errorf("%s:%s", utils.ErrServerError.Error(), "LCR_COMPUTE_ERRORS")
}
continue
}
if dtcs, err := utils.NewDTCSFromRPKey(qriedSuppl.Supplier); err != nil {
return utils.NewErrServerError(err)
} else {
lcrReply.Suppliers = append(lcrReply.Suppliers, &engine.LcrSupplier{Supplier: dtcs.Subject, Cost: qriedSuppl.Cost, QOS: qriedSuppl.QOS})
}
}
return nil
}
// Computes the LCR for a specific request emulating a call, returns a comma separated list of suppliers
func (self *ApierV1) GetLcrSuppliers(lcrReq engine.LcrRequest, suppliers *string) (err error) {
cd, err := lcrReq.AsCallDescriptor(self.Config.DefaultTimezone)
if err != nil {
return err
}
var lcrQried engine.LCRCost
if err := self.Responder.GetLCR(&engine.AttrGetLcr{CallDescriptor: cd, Paginator: lcrReq.Paginator}, &lcrQried); err != nil {
return utils.NewErrServerError(err)
}
if lcrQried.HasErrors() {
lcrQried.LogErrors()
if !lcrReq.IgnoreErrors {
return fmt.Errorf("%s:%s", utils.ErrServerError.Error(), "LCR_COMPUTE_ERRORS")
}
}
if suppliersStr, err := lcrQried.SuppliersString(); err != nil {
return utils.NewErrServerError(err)
} else {
*suppliers = suppliersStr
}
return nil
}

View File

@@ -1,6 +1,6 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2013 ITsysCOM
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
@@ -16,10 +16,4 @@ 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 cgrates
import (
_ "github.com/cgrates/cgrates/cmd/cgr-console"
_ "github.com/cgrates/cgrates/cmd/cgr-loader"
_ "github.com/cgrates/cgrates/cmd/cgr-rater"
)
package v1

171
apier/v1/scheduler.go Normal file
View File

@@ -0,0 +1,171 @@
/*
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 v1
import (
"errors"
"strings"
"time"
"github.com/cgrates/cgrates/utils"
)
/*
[
{
u'ActionsId': u'BONUS_1',
u'Uuid': u'5b5ba53b40b1d44380cce52379ec5c0d',
u'Weight': 10,
u'Timing': {
u'Timing': {
u'MonthDays': [
],
u'Months': [
],
u'WeekDays': [
],
u'Years': [
2013
],
u'StartTime': u'11: 00: 00',
u'EndTime': u''
},
u'Rating': None,
u'Weight': 0
},
u'AccountIds': [
u'*out: cgrates.org: 1001',
u'*out: cgrates.org: 1002',
u'*out: cgrates.org: 1003',
u'*out: cgrates.org: 1004',
u'*out: cgrates.org: 1005'
],
u'Id': u'PREPAID_10'
},
{
u'ActionsId': u'PREPAID_10',
u'Uuid': u'b16ab12740e2e6c380ff7660e8b55528',
u'Weight': 10,
u'Timing': {
u'Timing': {
u'MonthDays': [
],
u'Months': [
],
u'WeekDays': [
],
u'Years': [
2013
],
u'StartTime': u'11: 00: 00',
u'EndTime': u''
},
u'Rating': None,
u'Weight': 0
},
u'AccountIds': [
u'*out: cgrates.org: 1001',
u'*out: cgrates.org: 1002',
u'*out: cgrates.org: 1003',
u'*out: cgrates.org: 1004',
u'*out: cgrates.org: 1005'
],
u'Id': u'PREPAID_10'
}
]
*/
type AttrsGetScheduledActions struct {
Direction, Tenant, Account string
TimeStart, TimeEnd time.Time // Filter based on next runTime
utils.Paginator
}
type ScheduledActions struct {
NextRunTime time.Time
Accounts []*utils.DirectionTenantAccount
ActionsId, ActionPlanId, ActionPlanUuid string
}
func (self *ApierV1) GetScheduledActions(attrs AttrsGetScheduledActions, reply *[]*ScheduledActions) error {
schedActions := make([]*ScheduledActions, 0)
if self.Sched == nil {
return errors.New("SCHEDULER_NOT_ENABLED")
}
scheduledActions := self.Sched.GetQueue()
var min, max int
if attrs.Paginator.Offset != nil {
min = *attrs.Paginator.Offset
}
if attrs.Paginator.Limit != nil {
max = *attrs.Paginator.Limit
}
if max > len(scheduledActions) {
max = len(scheduledActions)
}
scheduledActions = scheduledActions[min : min+max]
for _, qActions := range scheduledActions {
sas := &ScheduledActions{ActionsId: qActions.ActionsId, ActionPlanId: qActions.Id, ActionPlanUuid: qActions.Uuid}
if attrs.SearchTerm != "" &&
!(strings.Contains(sas.ActionPlanId, attrs.SearchTerm) ||
strings.Contains(sas.ActionsId, attrs.SearchTerm)) {
continue
}
sas.NextRunTime = qActions.GetNextStartTime(time.Now())
if !attrs.TimeStart.IsZero() && sas.NextRunTime.Before(attrs.TimeStart) {
continue // Filter here only requests in the filtered interval
}
if !attrs.TimeEnd.IsZero() && (sas.NextRunTime.After(attrs.TimeEnd) || sas.NextRunTime.Equal(attrs.TimeEnd)) {
continue
}
acntFiltersMatch := false
for _, acntKey := range qActions.AccountIds {
directionMatched := len(attrs.Direction) == 0
tenantMatched := len(attrs.Tenant) == 0
accountMatched := len(attrs.Account) == 0
dta, _ := utils.NewDTAFromAccountKey(acntKey)
sas.Accounts = append(sas.Accounts, dta)
// One member matching
if !directionMatched && attrs.Direction == dta.Direction {
directionMatched = true
}
if !tenantMatched && attrs.Tenant == dta.Tenant {
tenantMatched = true
}
if !accountMatched && attrs.Account == dta.Account {
accountMatched = true
}
if directionMatched && tenantMatched && accountMatched {
acntFiltersMatch = true
}
}
if !acntFiltersMatch {
continue
}
schedActions = append(schedActions, sas)
}
*reply = schedActions
return nil
}

47
apier/v1/smv1.go Normal file
View File

@@ -0,0 +1,47 @@
/*
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 v1
import (
"github.com/cgrates/cgrates/sessionmanager"
"github.com/cgrates/cgrates/utils"
)
// Interact with SessionManager
type SessionManagerV1 struct {
SMs []sessionmanager.SessionManager // List of session managers since we support having more than one active session manager running on one host
}
func (self *SessionManagerV1) ActiveSessionMangers(ignored string, reply *[]sessionmanager.SessionManager) error {
if len(self.SMs) == 0 {
return utils.ErrNotFound
}
*reply = self.SMs
return nil
}
func (self *SessionManagerV1) ActiveSessions(attrs utils.AttrGetSMASessions, reply *[]*sessionmanager.ActiveSession) error {
if attrs.SessionManagerIndex > len(self.SMs)-1 {
return utils.ErrNotFound
}
for _, session := range self.SMs[attrs.SessionManagerIndex].Sessions() {
*reply = append(*reply, session.AsActiveSessions()...)
}
return nil
}

99
apier/v1/tp.go Normal file
View File

@@ -0,0 +1,99 @@
/*
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 v1
// Tariff plan related APIs
import (
"io/ioutil"
"os"
"path/filepath"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
type AttrGetTPIds struct {
}
// Queries tarrif plan identities gathered from all tables.
func (self *ApierV1) GetTPIds(attrs AttrGetTPIds, reply *[]string) error {
if ids, err := self.StorDb.GetTpIds(); err != nil {
return utils.NewErrServerError(err)
} else if ids == nil {
return utils.ErrNotFound
} else {
*reply = ids
}
return nil
}
type AttrImportTPZipFile struct {
TPid string
File []byte
}
func (self *ApierV1) ImportTPZipFile(attrs AttrImportTPZipFile, reply *string) error {
tmpDir, err := ioutil.TempDir("/tmp", "cgr_")
if err != nil {
*reply = "ERROR: creating temp directory!"
return err
}
zipFile := filepath.Join(tmpDir, "/file.zip")
if err = ioutil.WriteFile(zipFile, attrs.File, os.ModePerm); err != nil {
*reply = "ERROR: writing zip file!"
return err
}
if err = utils.Unzip(zipFile, tmpDir); err != nil {
*reply = "ERROR: unziping file!"
return err
}
csvfilesFound := false
if err = filepath.Walk(tmpDir, func(path string, info os.FileInfo, err error) error {
if !info.IsDir() {
return nil
}
csvFiles, err := filepath.Glob(filepath.Join(path, "*csv"))
if csvFiles != nil {
if attrs.TPid == "" {
*reply = "ERROR: missing TPid!"
return err
}
csvImporter := engine.TPCSVImporter{
TPid: attrs.TPid,
StorDb: self.StorDb,
DirPath: path,
Sep: ',',
Verbose: false,
ImportId: "",
}
if errImport := csvImporter.Run(); errImport != nil {
return errImport
}
csvfilesFound = true
}
return err
}); err != nil || !csvfilesFound {
*reply = "ERROR: finding csv files!"
return err
}
os.RemoveAll(tmpDir)
*reply = "OK"
return nil
}

View File

@@ -0,0 +1,166 @@
/*
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 v1
import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Creates a new AccountActions profile within a tariff plan
func (self *ApierV1) SetTPAccountActions(attrs utils.TPAccountActions, reply *string) error {
if missing := utils.MissingStructFields(&attrs,
[]string{"TPid", "LoadId", "Tenant", "Account", "Direction", "ActionPlanId", "ActionTriggersId"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
aas := engine.APItoModelAccountAction(&attrs)
if err := self.StorDb.SetTpAccountActions([]engine.TpAccountAction{*aas}); err != nil {
return utils.NewErrServerError(err)
}
*reply = "OK"
return nil
}
type AttrGetTPAccountActionsByLoadId struct {
TPid string // Tariff plan id
LoadId string // AccountActions id
}
// Queries specific AccountActions profile on tariff plan
func (self *ApierV1) GetTPAccountActionsByLoadId(attrs utils.TPAccountActions, reply *[]*utils.TPAccountActions) error {
mndtryFlds := []string{"TPid", "LoadId"}
if len(attrs.Account) != 0 { // If account provided as filter, make all related fields mandatory
mndtryFlds = append(mndtryFlds, "Tenant", "Account", "Direction")
}
if missing := utils.MissingStructFields(&attrs, mndtryFlds); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
aas := engine.APItoModelAccountAction(&attrs)
if aa, err := self.StorDb.GetTpAccountActions(aas); err != nil {
return utils.NewErrServerError(err)
} else if len(aa) == 0 {
return utils.ErrNotFound
} else {
tpAa, err := engine.TpAccountActions(aa).GetAccountActions()
if err != nil {
return err
}
var acts []*utils.TPAccountActions
if len(attrs.Account) != 0 {
acts = []*utils.TPAccountActions{tpAa[attrs.KeyId()]}
} else {
for _, actLst := range tpAa {
acts = append(acts, actLst)
}
}
*reply = acts
}
return nil
}
type AttrGetTPAccountActions struct {
TPid string // Tariff plan id
AccountActionsId string // DerivedCharge id
}
// Queries specific DerivedCharge on tariff plan
func (self *ApierV1) GetTPAccountActions(attrs AttrGetTPAccountActions, reply *utils.TPAccountActions) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "AccountActionsId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
tmpAa := &utils.TPAccountActions{TPid: attrs.TPid}
if err := tmpAa.SetAccountActionsId(attrs.AccountActionsId); err != nil {
return err
}
tmpAaa := engine.APItoModelAccountAction(tmpAa)
if aas, err := self.StorDb.GetTpAccountActions(tmpAaa); err != nil {
return utils.NewErrServerError(err)
} else if len(aas) == 0 {
return utils.ErrNotFound
} else {
tpAaa, err := engine.TpAccountActions(aas).GetAccountActions()
if err != nil {
return err
}
aa := tpAaa[tmpAa.KeyId()]
tpdc := utils.TPAccountActions{
TPid: attrs.TPid,
ActionPlanId: aa.ActionPlanId,
ActionTriggersId: aa.ActionTriggersId,
}
if err := tpdc.SetAccountActionsId(attrs.AccountActionsId); err != nil {
return err
}
*reply = tpdc
}
return nil
}
type AttrGetTPAccountActionIds struct {
TPid string // Tariff plan id
utils.Paginator
}
// Queries AccountActions identities on specific tariff plan.
func (self *ApierV1) GetTPAccountActionLoadIds(attrs AttrGetTPAccountActionIds, reply *[]string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBL_TP_ACCOUNT_ACTIONS, utils.TPDistinctIds{"loadid"}, nil, &attrs.Paginator); err != nil {
return utils.NewErrServerError(err)
} else if ids == nil {
return utils.ErrNotFound
} else {
*reply = ids
}
return nil
}
// Queries DerivedCharges identities on specific tariff plan.
func (self *ApierV1) GetTPAccountActionIds(attrs AttrGetTPAccountActionIds, reply *[]string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBL_TP_ACCOUNT_ACTIONS, utils.TPDistinctIds{"loadid", "direction", "tenant", "account"}, nil, &attrs.Paginator); err != nil {
return utils.NewErrServerError(err)
} else if ids == nil {
return utils.ErrNotFound
} else {
*reply = ids
}
return nil
}
// Removes specific AccountActions on Tariff plan
func (self *ApierV1) RemTPAccountActions(attrs AttrGetTPAccountActions, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "LoadId", "Tenant", "Account", "Direction"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
aa := engine.TpAccountAction{Tpid: attrs.TPid}
if err := aa.SetAccountActionId(attrs.AccountActionsId); err != nil {
return err
}
if err := self.StorDb.RemTpData(utils.TBL_TP_ACCOUNT_ACTIONS, aa.Tpid, aa.Loadid, aa.Direction, aa.Tenant, aa.Account); err != nil {
return utils.NewErrServerError(err)
} else {
*reply = "OK"
}
return nil
}

107
apier/v1/tpactionplans.go Normal file
View File

@@ -0,0 +1,107 @@
/*
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 v1
import (
"fmt"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Creates a new ActionTimings profile within a tariff plan
func (self *ApierV1) SetTPActionPlan(attrs utils.TPActionPlan, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "ActionPlanId", "ActionPlan"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
for _, at := range attrs.ActionPlan {
requiredFields := []string{"ActionsId", "TimingId", "Weight"}
if missing := utils.MissingStructFields(at, requiredFields); len(missing) != 0 {
return fmt.Errorf("%s:Action:%s:%v", utils.ErrMandatoryIeMissing.Error(), at.ActionsId, missing)
}
}
ap := engine.APItoModelActionPlan(&attrs)
if err := self.StorDb.SetTpActionPlans(ap); err != nil {
return utils.NewErrServerError(err)
}
*reply = "OK"
return nil
}
type AttrGetTPActionPlan struct {
TPid string // Tariff plan id
Id string // ActionPlans id
}
// Queries specific ActionPlan profile on tariff plan
func (self *ApierV1) GetTPActionPlan(attrs AttrGetTPActionPlan, reply *utils.TPActionPlan) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "Id"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if ats, err := self.StorDb.GetTpActionPlans(attrs.TPid, attrs.Id); err != nil {
return utils.NewErrServerError(err)
} else if len(ats) == 0 {
return utils.ErrNotFound
} else { // Got the data we need, convert it
aps, err := engine.TpActionPlans(ats).GetActionPlans()
if err != nil {
return err
}
atRply := &utils.TPActionPlan{
TPid: attrs.TPid,
ActionPlanId: attrs.Id,
ActionPlan: aps[attrs.Id],
}
*reply = *atRply
}
return nil
}
type AttrGetTPActionPlanIds struct {
TPid string // Tariff plan id
utils.Paginator
}
// Queries ActionPlan identities on specific tariff plan.
func (self *ApierV1) GetTPActionPlanIds(attrs AttrGetTPActionPlanIds, reply *[]string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBL_TP_ACTION_PLANS, utils.TPDistinctIds{"tag"}, nil, &attrs.Paginator); err != nil {
return utils.NewErrServerError(err)
} else if ids == nil {
return utils.ErrNotFound
} else {
*reply = ids
}
return nil
}
// Removes specific ActionPlan on Tariff plan
func (self *ApierV1) RemTPActionPlan(attrs AttrGetTPActionPlan, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "Id"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if err := self.StorDb.RemTpData(utils.TBL_TP_ACTION_PLANS, attrs.TPid, attrs.Id); err != nil {
return utils.NewErrServerError(err)
} else {
*reply = "OK"
}
return nil
}

105
apier/v1/tpactions.go Normal file
View File

@@ -0,0 +1,105 @@
/*
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 v1
import (
"fmt"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Creates a new Actions profile within a tariff plan
func (self *ApierV1) SetTPActions(attrs utils.TPActions, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "ActionsId", "Actions"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
for _, action := range attrs.Actions {
requiredFields := []string{"Identifier", "Weight"}
if action.BalanceType != "" { // Add some inter-dependent parameters - if balanceType then we are not talking about simply calling actions
requiredFields = append(requiredFields, "Direction", "Units")
}
if missing := utils.MissingStructFields(action, requiredFields); len(missing) != 0 {
return fmt.Errorf("%s:Action:%s:%v", utils.ErrMandatoryIeMissing.Error(), action.Identifier, missing)
}
}
as := engine.APItoModelAction(&attrs)
if err := self.StorDb.SetTpActions(as); err != nil {
return utils.NewErrServerError(err)
}
*reply = "OK"
return nil
}
type AttrGetTPActions struct {
TPid string // Tariff plan id
ActionsId string // Actions id
}
// Queries specific Actions profile on tariff plan
func (self *ApierV1) GetTPActions(attrs AttrGetTPActions, reply *utils.TPActions) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "ActionsId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if acts, err := self.StorDb.GetTpActions(attrs.TPid, attrs.ActionsId); err != nil {
return utils.NewErrServerError(err)
} else if len(acts) == 0 {
return utils.ErrNotFound
} else {
as, err := engine.TpActions(acts).GetActions()
if err != nil {
}
*reply = utils.TPActions{TPid: attrs.TPid, ActionsId: attrs.ActionsId, Actions: as[attrs.ActionsId]}
}
return nil
}
type AttrGetTPActionIds struct {
TPid string // Tariff plan id
utils.Paginator
}
// Queries Actions identities on specific tariff plan.
func (self *ApierV1) GetTPActionIds(attrs AttrGetTPActionIds, reply *[]string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBL_TP_ACTIONS, utils.TPDistinctIds{"tag"}, nil, &attrs.Paginator); err != nil {
return utils.NewErrServerError(err)
} else if ids == nil {
return utils.ErrNotFound
} else {
*reply = ids
}
return nil
}
// Removes specific Actions on Tariff plan
func (self *ApierV1) RemTPActions(attrs AttrGetTPActions, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "ActionsId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if err := self.StorDb.RemTpData(utils.TBL_TP_ACTIONS, attrs.TPid, attrs.ActionsId); err != nil {
return utils.NewErrServerError(err)
} else {
*reply = "OK"
}
return nil
}

View File

@@ -0,0 +1,101 @@
/*
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 v1
import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Creates a new ActionTriggers profile within a tariff plan
func (self *ApierV1) SetTPActionTriggers(attrs utils.TPActionTriggers, reply *string) error {
if missing := utils.MissingStructFields(&attrs,
[]string{"TPid", "ActionTriggersId"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
ats := engine.APItoModelActionTrigger(&attrs)
if err := self.StorDb.SetTpActionTriggers(ats); err != nil {
return utils.NewErrServerError(err)
}
*reply = "OK"
return nil
}
type AttrGetTPActionTriggers struct {
TPid string // Tariff plan id
ActionTriggersId string // ActionTrigger id
}
// Queries specific ActionTriggers profile on tariff plan
func (self *ApierV1) GetTPActionTriggers(attrs AttrGetTPActionTriggers, reply *utils.TPActionTriggers) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "ActionTriggersId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if ats, err := self.StorDb.GetTpActionTriggers(attrs.TPid, attrs.ActionTriggersId); err != nil {
return utils.NewErrServerError(err)
} else if len(ats) == 0 {
return utils.ErrNotFound
} else {
atsMap, err := engine.TpActionTriggers(ats).GetActionTriggers()
if err != nil {
return err
}
atRply := &utils.TPActionTriggers{
TPid: attrs.TPid,
ActionTriggersId: attrs.ActionTriggersId,
ActionTriggers: atsMap[attrs.ActionTriggersId],
}
*reply = *atRply
}
return nil
}
type AttrGetTPActionTriggerIds struct {
TPid string // Tariff plan id
utils.Paginator
}
// Queries ActionTriggers identities on specific tariff plan.
func (self *ApierV1) GetTPActionTriggerIds(attrs AttrGetTPActionTriggerIds, reply *[]string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBL_TP_ACTION_TRIGGERS, utils.TPDistinctIds{"tag"}, nil, &attrs.Paginator); err != nil {
return utils.NewErrServerError(err)
} else if ids == nil {
return utils.ErrNotFound
} else {
*reply = ids
}
return nil
}
// Removes specific ActionTriggers on Tariff plan
func (self *ApierV1) RemTPActionTriggers(attrs AttrGetTPActionTriggers, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "ActionTriggersId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if err := self.StorDb.RemTpData(utils.TBL_TP_ACTION_TRIGGERS, attrs.TPid, attrs.ActionTriggersId); err != nil {
return utils.NewErrServerError(err)
} else {
*reply = "OK"
}
return nil
}

96
apier/v1/tpaliases.go Normal file
View File

@@ -0,0 +1,96 @@
/*
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 v1
import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Creates a new alias within a tariff plan
func (self *ApierV1) SetTPAlias(attrs utils.TPAliases, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "Direction", "Tenant", "Category", "Account", "Subject", "Group"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
tm := engine.APItoModelAliases(&attrs)
if err := self.StorDb.SetTpAliases(tm); err != nil {
return utils.NewErrServerError(err)
}
*reply = "OK"
return nil
}
type AttrGetTPAlias struct {
TPid string // Tariff plan id
AliasId string
}
// Queries specific Alias on Tariff plan
func (self *ApierV1) GetTPAlias(attr AttrGetTPAlias, reply *utils.TPAliases) error {
if missing := utils.MissingStructFields(&attr, []string{"TPid"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
al := &engine.TpAlias{Tpid: attr.TPid}
al.SetId(attr.AliasId)
if tms, err := self.StorDb.GetTpAliases(al); err != nil {
return utils.NewErrServerError(err)
} else if len(tms) == 0 {
return utils.ErrNotFound
} else {
tmMap, err := engine.TpAliases(tms).GetAliases()
if err != nil {
return err
}
*reply = *tmMap[al.GetId()]
}
return nil
}
type AttrGetTPAliasIds struct {
TPid string // Tariff plan id
utils.Paginator
}
// Queries alias identities on specific tariff plan.
func (self *ApierV1) GetTPAliasIds(attrs AttrGetTPAliasIds, reply *[]string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBL_TP_ALIASES, utils.TPDistinctIds{"`direction`", "`tenant`", "`category`", "`account`", "`subject`", "`group`"}, nil, &attrs.Paginator); err != nil {
return utils.NewErrServerError(err)
} else if ids == nil {
return utils.ErrNotFound
} else {
*reply = ids
}
return nil
}
// Removes specific Alias on Tariff plan
func (self *ApierV1) RemTPAlias(attrs AttrGetTPAlias, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "AliasId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if err := self.StorDb.RemTpData(utils.TBL_TP_ALIASES, attrs.TPid); err != nil {
return utils.NewErrServerError(err)
} else {
*reply = "OK"
}
return nil
}

103
apier/v1/tpcdrstats.go Normal file
View File

@@ -0,0 +1,103 @@
/*
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 v1
import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Creates a new CdrStats profile within a tariff plan
func (self *ApierV1) SetTPCdrStats(attrs utils.TPCdrStats, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "CdrStatsId", "CdrStats"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
/*for _, action := range attrs.CdrStats {
requiredFields := []string{"Identifier", "Weight"}
if action.BalanceType != "" { // Add some inter-dependent parameters - if balanceType then we are not talking about simply calling actions
requiredFields = append(requiredFields, "Direction", "Units")
}
if missing := utils.MissingStructFields(action, requiredFields); len(missing) != 0 {
return fmt.Errorf("%s:CdrStat:%s:%v", utils.ERR_MANDATORY_IE_MISSING, action.Identifier, missing)
}
}*/
cs := engine.APItoModelCdrStat(&attrs)
if err := self.StorDb.SetTpCdrStats(cs); err != nil {
return utils.NewErrServerError(err)
}
*reply = "OK"
return nil
}
type AttrGetTPCdrStats struct {
TPid string // Tariff plan id
CdrStatsId string // CdrStat id
}
// Queries specific CdrStat on tariff plan
func (self *ApierV1) GetTPCdrStats(attrs AttrGetTPCdrStats, reply *utils.TPCdrStats) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "CdrStatsId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if sgs, err := self.StorDb.GetTpCdrStats(attrs.TPid, attrs.CdrStatsId); err != nil {
return utils.NewErrServerError(err)
} else if len(sgs) == 0 {
return utils.ErrNotFound
} else {
csMap, err := engine.TpCdrStats(sgs).GetCdrStats()
if err != nil {
return err
}
*reply = utils.TPCdrStats{TPid: attrs.TPid, CdrStatsId: attrs.CdrStatsId, CdrStats: csMap[attrs.CdrStatsId]}
}
return nil
}
type AttrGetTPCdrStatIds struct {
TPid string // Tariff plan id
utils.Paginator
}
// Queries CdrStats identities on specific tariff plan.
func (self *ApierV1) GetTPCdrStatsIds(attrs AttrGetTPCdrStatIds, reply *[]string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBL_TP_CDR_STATS, utils.TPDistinctIds{"tag"}, nil, &attrs.Paginator); err != nil {
return utils.NewErrServerError(err)
} else if ids == nil {
return utils.ErrNotFound
} else {
*reply = ids
}
return nil
}
// Removes specific CdrStats on Tariff plan
func (self *ApierV1) RemTPCdrStats(attrs AttrGetTPCdrStats, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "CdrStatsId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if err := self.StorDb.RemTpData(utils.TBL_TP_SHARED_GROUPS, attrs.TPid, attrs.CdrStatsId); err != nil {
return utils.NewErrServerError(err)
} else {
*reply = "OK"
}
return nil
}

View File

@@ -0,0 +1,112 @@
/*
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 v1
import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Creates a new DerivedCharges profile within a tariff plan
func (self *ApierV1) SetTPDerivedChargers(attrs utils.TPDerivedChargers, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "Direction", "Tenant", "Category", "Account", "Subject"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
/*for _, action := range attrs.DerivedCharges {
requiredFields := []string{"Identifier", "Weight"}
if action.BalanceType != "" { // Add some inter-dependent parameters - if balanceType then we are not talking about simply calling actions
requiredFields = append(requiredFields, "Direction", "Units")
}
if missing := utils.MissingStructFields(action, requiredFields); len(missing) != 0 {
return fmt.Errorf("%s:DerivedCharge:%s:%v", utils.ERR_MANDATORY_IE_MISSING, action.Identifier, missing)
}
}*/
dc := engine.APItoModelDerivedCharger(&attrs)
if err := self.StorDb.SetTpDerivedChargers(dc); err != nil {
return utils.NewErrServerError(err)
}
*reply = "OK"
return nil
}
type AttrGetTPDerivedChargers struct {
TPid string // Tariff plan id
DerivedChargersId string // DerivedCharge id
}
// Queries specific DerivedCharge on tariff plan
func (self *ApierV1) GetTPDerivedChargers(attrs AttrGetTPDerivedChargers, reply *utils.TPDerivedChargers) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "DerivedChargersId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
tmpDc := &utils.TPDerivedChargers{TPid: attrs.TPid}
if err := tmpDc.SetDerivedChargersId(attrs.DerivedChargersId); err != nil {
return err
}
dcs := engine.APItoModelDerivedCharger(tmpDc)
if sgs, err := self.StorDb.GetTpDerivedChargers(&dcs[0]); err != nil {
return utils.NewErrServerError(err)
} else if len(sgs) == 0 {
return utils.ErrNotFound
} else {
dcsMap, err := engine.TpDerivedChargers(dcs).GetDerivedChargers()
if err != nil {
return err
}
*reply = *dcsMap[attrs.DerivedChargersId]
}
return nil
}
type AttrGetTPDerivedChargeIds struct {
TPid string // Tariff plan id
utils.Paginator
}
// Queries DerivedCharges identities on specific tariff plan.
func (self *ApierV1) GetTPDerivedChargerIds(attrs AttrGetTPDerivedChargeIds, reply *[]string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBL_TP_DERIVED_CHARGERS, utils.TPDistinctIds{"loadid", "direction", "tenant", "category", "account", "subject"}, nil, &attrs.Paginator); err != nil {
return utils.NewErrServerError(err)
} else if ids == nil {
return utils.ErrNotFound
} else {
*reply = ids
}
return nil
}
// Removes specific DerivedCharges on Tariff plan
func (self *ApierV1) RemTPDerivedChargers(attrs AttrGetTPDerivedChargers, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "DerivedChargesId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
tmpDc := engine.TpDerivedCharger{}
if err := tmpDc.SetDerivedChargersId(attrs.DerivedChargersId); err != nil {
return err
}
if err := self.StorDb.RemTpData(utils.TBL_TP_DERIVED_CHARGERS, attrs.TPid, tmpDc.Loadid, tmpDc.Direction, tmpDc.Tenant, tmpDc.Category, tmpDc.Account, tmpDc.Subject); err != nil {
return utils.NewErrServerError(err)
} else {
*reply = "OK"
}
return nil
}

View File

@@ -0,0 +1,97 @@
/*
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 v1
// This file deals with tp_destination_rates management over APIs
import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Creates a new DestinationRate profile within a tariff plan
func (self *ApierV1) SetTPDestinationRate(attrs utils.TPDestinationRate, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "DestinationRateId", "DestinationRates"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
drs := engine.APItoModelDestinationRate(&attrs)
if err := self.StorDb.SetTpDestinationRates(drs); err != nil {
return utils.NewErrServerError(err)
}
*reply = "OK"
return nil
}
type AttrGetTPDestinationRate struct {
TPid string // Tariff plan id
DestinationRateId string // Rate id
utils.Paginator
}
// Queries specific DestinationRate profile on tariff plan
func (self *ApierV1) GetTPDestinationRate(attrs AttrGetTPDestinationRate, reply *utils.TPDestinationRate) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "DestinationRateId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if drs, err := self.StorDb.GetTpDestinationRates(attrs.TPid, attrs.DestinationRateId, &attrs.Paginator); err != nil {
return utils.NewErrServerError(err)
} else if len(drs) == 0 {
return utils.ErrNotFound
} else {
drsMap, err := engine.TpDestinationRates(drs).GetDestinationRates()
if err != nil {
return err
}
*reply = *drsMap[attrs.DestinationRateId]
}
return nil
}
type AttrTPDestinationRateIds struct {
TPid string // Tariff plan id
utils.Paginator
}
// Queries DestinationRate identities on specific tariff plan.
func (self *ApierV1) GetTPDestinationRateIds(attrs AttrGetTPRateIds, reply *[]string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBL_TP_DESTINATION_RATES, utils.TPDistinctIds{"tag"}, nil, &attrs.Paginator); err != nil {
return utils.NewErrServerError(err)
} else if ids == nil {
return utils.ErrNotFound
} else {
*reply = ids
}
return nil
}
// Removes specific DestinationRate on Tariff plan
func (self *ApierV1) RemTPDestinationRate(attrs AttrGetTPDestinationRate, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "DestinationRateId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if err := self.StorDb.RemTpData(utils.TBL_TP_DESTINATION_RATES, attrs.TPid, attrs.DestinationRateId); err != nil {
return utils.NewErrServerError(err)
} else {
*reply = "OK"
}
return nil
}

View File

@@ -0,0 +1,97 @@
/*
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 v1
import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Creates a new destination within a tariff plan
func (self *ApierV1) SetTPDestination(attrs utils.TPDestination, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "DestinationId", "Prefixes"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
ds := engine.APItoModelDestination(&attrs)
if err := self.StorDb.SetTpDestinations(ds); err != nil {
return utils.NewErrServerError(err)
}
*reply = "OK"
return nil
}
type AttrGetTPDestination struct {
TPid string // Tariff plan id
DestinationId string // Destination id
}
// Queries a specific destination
func (self *ApierV1) GetTPDestination(attrs AttrGetTPDestination, reply *utils.TPDestination) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "DestinationId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if storData, err := self.StorDb.GetTpDestinations(attrs.TPid, attrs.DestinationId); err != nil {
return utils.NewErrServerError(err)
} else if len(storData) == 0 {
return utils.ErrNotFound
} else {
dsts, err := engine.TpDestinations(storData).GetDestinations()
if err != nil {
return err
}
*reply = utils.TPDestination{
TPid: attrs.TPid,
DestinationId: dsts[attrs.DestinationId].Id,
Prefixes: dsts[attrs.DestinationId].Prefixes}
}
return nil
}
type AttrGetTPDestinationIds struct {
TPid string // Tariff plan id
utils.Paginator
}
// Queries destination identities on specific tariff plan.
func (self *ApierV1) GetTPDestinationIds(attrs AttrGetTPDestinationIds, reply *[]string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBL_TP_DESTINATIONS, utils.TPDistinctIds{"tag"}, nil, &attrs.Paginator); err != nil {
return utils.NewErrServerError(err)
} else if ids == nil {
return utils.ErrNotFound
} else {
*reply = ids
}
return nil
}
func (self *ApierV1) RemTPDestination(attrs AttrGetTPDestination, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "DestinationId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if err := self.StorDb.RemTpData(utils.TBL_TP_DESTINATIONS, attrs.TPid, attrs.DestinationId); err != nil {
return utils.NewErrServerError(err)
} else {
*reply = "OK"
}
return nil
}

98
apier/v1/tplcrrules.go Normal file
View File

@@ -0,0 +1,98 @@
/*
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 v1
import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Creates a new LcrRules profile within a tariff plan
func (self *ApierV1) SetTPLcrRule(attrs utils.TPLcrRules, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "Direction", "Tenant", "Category", "Account", "Subject"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
tm := engine.APItoModelLcrRule(&attrs)
if err := self.StorDb.SetTpLCRs(tm); err != nil {
return utils.NewErrServerError(err)
}
*reply = "OK"
return nil
}
type AttrGetTPLcrRules struct {
TPid string // Tariff plan id
LcrRuleId string // Lcr id
}
// Queries specific LcrRules profile on tariff plan
func (self *ApierV1) GetTPLcrRule(attr AttrGetTPLcrRules, reply *utils.TPLcrRules) error {
if missing := utils.MissingStructFields(&attr, []string{"TPid", "LcrRuleId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
lcr := &engine.TpLcrRule{
Tpid: attr.TPid,
}
lcr.SetLcrRuleId(attr.LcrRuleId)
if lcrs, err := self.StorDb.GetTpLCRs(lcr); err != nil {
return utils.NewErrServerError(err)
} else if len(lcrs) == 0 {
return utils.ErrNotFound
} else {
tmMap, err := engine.TpLcrRules(lcrs).GetLcrRules()
if err != nil {
return err
}
*reply = *tmMap[attr.LcrRuleId]
}
return nil
}
type AttrGetTPLcrIds struct {
TPid string // Tariff plan id
utils.Paginator
}
// Queries LcrRules identities on specific tariff plan.
func (self *ApierV1) GetTPLcrRuleIds(attrs AttrGetTPLcrIds, reply *[]string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBL_TP_LCRS, utils.TPDistinctIds{"direction", "tenant", "category", "account", "subject"}, nil, &attrs.Paginator); err != nil {
return utils.NewErrServerError(err)
} else if ids == nil {
return utils.ErrNotFound
} else {
*reply = ids
}
return nil
}
// Removes specific LcrRules on Tariff plan
func (self *ApierV1) RemTPLcrRule(attrs AttrGetTPLcrRules, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "LcrRulesId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if err := self.StorDb.RemTpData(utils.TBL_TP_LCRS, attrs.TPid, attrs.LcrRuleId); err != nil {
return utils.NewErrServerError(err)
} else {
*reply = "OK"
}
return nil
}

96
apier/v1/tprates.go Normal file
View File

@@ -0,0 +1,96 @@
/*
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 v1
// This file deals with tp_rates management over APIs
import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Creates a new rate within a tariff plan
func (self *ApierV1) SetTPRate(attrs utils.TPRate, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "RateId", "RateSlots"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
r := engine.APItoModelRate(&attrs)
if err := self.StorDb.SetTpRates(r); err != nil {
return utils.NewErrServerError(err)
}
*reply = "OK"
return nil
}
type AttrGetTPRate struct {
TPid string // Tariff plan id
RateId string // Rate id
}
// Queries specific Rate on tariff plan
func (self *ApierV1) GetTPRate(attrs AttrGetTPRate, reply *utils.TPRate) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "RateId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if rts, err := self.StorDb.GetTpRates(attrs.TPid, attrs.RateId); err != nil {
return utils.NewErrServerError(err)
} else if len(rts) == 0 {
return utils.ErrNotFound
} else {
rtsMap, err := engine.TpRates(rts).GetRates()
if err != nil {
return err
}
*reply = *rtsMap[attrs.RateId]
}
return nil
}
type AttrGetTPRateIds struct {
TPid string // Tariff plan id
utils.Paginator
}
// Queries rate identities on specific tariff plan.
func (self *ApierV1) GetTPRateIds(attrs AttrGetTPRateIds, reply *[]string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBL_TP_RATES, utils.TPDistinctIds{"tag"}, nil, &attrs.Paginator); err != nil {
return utils.NewErrServerError(err)
} else if ids == nil {
return utils.ErrNotFound
} else {
*reply = ids
}
return nil
}
// Removes specific Rate on Tariff plan
func (self *ApierV1) RemTPRate(attrs AttrGetTPRate, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "RateId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if err := self.StorDb.RemTpData(utils.TBL_TP_RATES, attrs.TPid, attrs.RateId); err != nil {
return utils.NewErrServerError(err)
} else {
*reply = "OK"
}
return nil
}

97
apier/v1/tpratingplans.go Normal file
View File

@@ -0,0 +1,97 @@
/*
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 v1
// This file deals with tp_destrates_timing management over APIs
import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Creates a new DestinationRateTiming profile within a tariff plan
func (self *ApierV1) SetTPRatingPlan(attrs utils.TPRatingPlan, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "RatingPlanId", "RatingPlanBindings"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
rp := engine.APItoModelRatingPlan(&attrs)
if err := self.StorDb.SetTpRatingPlans(rp); err != nil {
return utils.NewErrServerError(err)
}
*reply = "OK"
return nil
}
type AttrGetTPRatingPlan struct {
TPid string // Tariff plan id
RatingPlanId string // Rate id
utils.Paginator
}
// Queries specific RatingPlan profile on tariff plan
func (self *ApierV1) GetTPRatingPlan(attrs AttrGetTPRatingPlan, reply *utils.TPRatingPlan) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "RatingPlanId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if rps, err := self.StorDb.GetTpRatingPlans(attrs.TPid, attrs.RatingPlanId, &attrs.Paginator); err != nil {
return utils.NewErrServerError(err)
} else if len(rps) == 0 {
return utils.ErrNotFound
} else {
rpsMap, err := engine.TpRatingPlans(rps).GetRatingPlans()
if err != nil {
return err
}
*reply = utils.TPRatingPlan{TPid: attrs.TPid, RatingPlanId: attrs.RatingPlanId, RatingPlanBindings: rpsMap[attrs.RatingPlanId]}
}
return nil
}
type AttrGetTPRatingPlanIds struct {
TPid string // Tariff plan id
utils.Paginator
}
// Queries RatingPlan identities on specific tariff plan.
func (self *ApierV1) GetTPRatingPlanIds(attrs AttrGetTPRatingPlanIds, reply *[]string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBL_TP_RATING_PLANS, utils.TPDistinctIds{"tag"}, nil, &attrs.Paginator); err != nil {
return utils.NewErrServerError(err)
} else if ids == nil {
return utils.ErrNotFound
} else {
*reply = ids
}
return nil
}
// Removes specific RatingPlan on Tariff plan
func (self *ApierV1) RemTPRatingPlan(attrs AttrGetTPRatingPlan, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "RatingPlanId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if err := self.StorDb.RemTpData(utils.TBL_TP_RATING_PLANS, attrs.TPid, attrs.RatingPlanId); err != nil {
return utils.NewErrServerError(err)
} else {
*reply = "OK"
}
return nil
}

View File

@@ -0,0 +1,170 @@
/*
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 v1
// This file deals with tp_rate_profiles management over APIs
import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Creates a new RatingProfile within a tariff plan
func (self *ApierV1) SetTPRatingProfile(attrs utils.TPRatingProfile, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "LoadId", "Tenant", "Category", "Direction", "Subject", "RatingPlanActivations"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
rpf := engine.APItoModelRatingProfile(&attrs)
if err := self.StorDb.SetTpRatingProfiles(rpf); err != nil {
return utils.NewErrServerError(err)
}
*reply = "OK"
return nil
}
type AttrGetTPRatingProfileByLoadId struct {
TPid string // Tariff plan id
LoadId string // RatingProfile id
}
// Queries specific RatingProfile on tariff plan
func (self *ApierV1) GetTPRatingProfilesByLoadId(attrs utils.TPRatingProfile, reply *[]*utils.TPRatingProfile) error {
mndtryFlds := []string{"TPid", "LoadId"}
if len(attrs.Subject) != 0 { // If Subject provided as filter, make all related fields mandatory
mndtryFlds = append(mndtryFlds, "Tenant", "TOR", "Direction", "Subject")
}
if missing := utils.MissingStructFields(&attrs, mndtryFlds); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
rpf := engine.APItoModelRatingProfile(&attrs)
if dr, err := self.StorDb.GetTpRatingProfiles(&rpf[0]); err != nil {
return utils.NewErrServerError(err)
} else if dr == nil {
return utils.ErrNotFound
} else {
rpfMap, err := engine.TpRatingProfiles(dr).GetRatingProfiles()
if err != nil {
return err
}
var rpfs []*utils.TPRatingProfile
if len(attrs.Subject) != 0 {
rpfs = []*utils.TPRatingProfile{rpfMap[attrs.KeyId()]}
} else {
for _, rpfLst := range rpfMap {
rpfs = append(rpfs, rpfLst)
}
}
*reply = rpfs
}
return nil
}
// Queries RatingProfile identities on specific tariff plan.
func (self *ApierV1) GetTPRatingProfileLoadIds(attrs utils.AttrTPRatingProfileIds, reply *[]string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBL_TP_RATE_PROFILES, utils.TPDistinctIds{"loadid"}, map[string]string{
"tenant": attrs.Tenant,
"tor": attrs.Category,
"direction": attrs.Direction,
"subject": attrs.Subject,
}, new(utils.Paginator)); err != nil {
return utils.NewErrServerError(err)
} else if ids == nil {
return utils.ErrNotFound
} else {
*reply = ids
}
return nil
}
type AttrGetTPRatingProfile struct {
TPid string // Tariff plan id
RatingProfileId string // RatingProfile id
}
// Queries specific RatingProfile on tariff plan
func (self *ApierV1) GetTPRatingProfile(attrs AttrGetTPRatingProfile, reply *utils.TPRatingProfile) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "RatingProfileId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
tmpRpf := &utils.TPRatingProfile{TPid: attrs.TPid}
if err := tmpRpf.SetRatingProfilesId(attrs.RatingProfileId); err != nil {
return err
}
rpf := engine.APItoModelRatingProfile(tmpRpf)
if rpfs, err := self.StorDb.GetTpRatingProfiles(&rpf[0]); err != nil {
return utils.NewErrServerError(err)
} else if len(rpfs) == 0 {
return utils.ErrNotFound
} else {
rpfMap, err := engine.TpRatingProfiles(rpfs).GetRatingProfiles()
if err != nil {
return err
}
rpf := rpfMap[tmpRpf.KeyId()]
tpdc := utils.TPRatingProfile{
TPid: attrs.TPid,
RatingPlanActivations: rpf.RatingPlanActivations,
}
if err := tpdc.SetRatingProfilesId(attrs.RatingProfileId); err != nil {
return err
}
*reply = tpdc
}
return nil
}
type AttrGetTPRatingProfileIds struct {
TPid string // Tariff plan id
utils.Paginator
}
// Queries RatingProfiles identities on specific tariff plan.
func (self *ApierV1) GetTPRatingProfileIds(attrs AttrGetTPRatingProfileIds, reply *[]string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBL_TP_RATE_PROFILES, utils.TPDistinctIds{"loadid", "direction", "tenant", "category", "subject"}, nil, &attrs.Paginator); err != nil {
return utils.NewErrServerError(err)
} else if ids == nil {
return utils.ErrNotFound
} else {
*reply = ids
}
return nil
}
// Removes specific RatingProfiles on Tariff plan
func (self *ApierV1) RemTPRatingProfile(attrs AttrGetTPRatingProfile, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "RatingProfileId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
tmpRpf := engine.TpRatingProfile{}
if err := tmpRpf.SetRatingProfileId(attrs.RatingProfileId); err != nil {
return err
}
if err := self.StorDb.RemTpData(utils.TBL_TP_RATE_PROFILES, attrs.TPid, tmpRpf.Loadid, tmpRpf.Direction, tmpRpf.Tenant, tmpRpf.Category, tmpRpf.Subject); err != nil {
return utils.NewErrServerError(err)
} else {
*reply = "OK"
}
return nil
}

103
apier/v1/tpsharedgroups.go Normal file
View File

@@ -0,0 +1,103 @@
/*
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 v1
import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Creates a new SharedGroups profile within a tariff plan
func (self *ApierV1) SetTPSharedGroups(attrs utils.TPSharedGroups, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "SharedGroupsId", "SharedGroups"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
/*for _, action := range attrs.SharedGroups {
requiredFields := []string{"Identifier", "Weight"}
if action.BalanceType != "" { // Add some inter-dependent parameters - if balanceType then we are not talking about simply calling actions
requiredFields = append(requiredFields, "Direction", "Units")
}
if missing := utils.MissingStructFields(action, requiredFields); len(missing) != 0 {
return fmt.Errorf("%s:SharedGroup:%s:%v", utils.ERR_MANDATORY_IE_MISSING, action.Identifier, missing)
}
}*/
sg := engine.APItoModelSharedGroup(&attrs)
if err := self.StorDb.SetTpSharedGroups(sg); err != nil {
return utils.NewErrServerError(err)
}
*reply = "OK"
return nil
}
type AttrGetTPSharedGroups struct {
TPid string // Tariff plan id
SharedGroupsId string // SharedGroup id
}
// Queries specific SharedGroup on tariff plan
func (self *ApierV1) GetTPSharedGroups(attrs AttrGetTPSharedGroups, reply *utils.TPSharedGroups) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "SharedGroupsId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if sgs, err := self.StorDb.GetTpSharedGroups(attrs.TPid, attrs.SharedGroupsId); err != nil {
return utils.NewErrServerError(err)
} else if len(sgs) == 0 {
return utils.ErrNotFound
} else {
sgMap, err := engine.TpSharedGroups(sgs).GetSharedGroups()
if err != nil {
return err
}
*reply = utils.TPSharedGroups{TPid: attrs.TPid, SharedGroupsId: attrs.SharedGroupsId, SharedGroups: sgMap[attrs.SharedGroupsId]}
}
return nil
}
type AttrGetTPSharedGroupIds struct {
TPid string // Tariff plan id
utils.Paginator
}
// Queries SharedGroups identities on specific tariff plan.
func (self *ApierV1) GetTPSharedGroupIds(attrs AttrGetTPSharedGroupIds, reply *[]string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBL_TP_SHARED_GROUPS, utils.TPDistinctIds{"tag"}, nil, &attrs.Paginator); err != nil {
return utils.NewErrServerError(err)
} else if ids == nil {
return utils.ErrNotFound
} else {
*reply = ids
}
return nil
}
// Removes specific SharedGroups on Tariff plan
func (self *ApierV1) RemTPSharedGroups(attrs AttrGetTPSharedGroups, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "SharedGroupsId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if err := self.StorDb.RemTpData(utils.TBL_TP_SHARED_GROUPS, attrs.TPid, attrs.SharedGroupsId); err != nil {
return utils.NewErrServerError(err)
} else {
*reply = "OK"
}
return nil
}

94
apier/v1/tptimings.go Normal file
View File

@@ -0,0 +1,94 @@
/*
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 v1
import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Creates a new timing within a tariff plan
func (self *ApierV1) SetTPTiming(attrs utils.ApierTPTiming, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "TimingId", "Years", "Months", "MonthDays", "WeekDays", "Time"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
tm := engine.APItoModelTiming(&attrs)
if err := self.StorDb.SetTpTimings([]engine.TpTiming{*tm}); err != nil {
return utils.NewErrServerError(err)
}
*reply = "OK"
return nil
}
type AttrGetTPTiming struct {
TPid string // Tariff plan id
TimingId string // Timing id
}
// Queries specific Timing on Tariff plan
func (self *ApierV1) GetTPTiming(attrs AttrGetTPTiming, reply *utils.ApierTPTiming) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "TimingId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if tms, err := self.StorDb.GetTpTimings(attrs.TPid, attrs.TimingId); err != nil {
return utils.NewErrServerError(err)
} else if len(tms) == 0 {
return utils.ErrNotFound
} else {
tmMap, err := engine.TpTimings(tms).GetApierTimings()
if err != nil {
return err
}
*reply = *tmMap[attrs.TimingId]
}
return nil
}
type AttrGetTPTimingIds struct {
TPid string // Tariff plan id
utils.Paginator
}
// Queries timing identities on specific tariff plan.
func (self *ApierV1) GetTPTimingIds(attrs AttrGetTPTimingIds, reply *[]string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBL_TP_TIMINGS, utils.TPDistinctIds{"tag"}, nil, &attrs.Paginator); err != nil {
return utils.NewErrServerError(err)
} else if ids == nil {
return utils.ErrNotFound
} else {
*reply = ids
}
return nil
}
// Removes specific Timing on Tariff plan
func (self *ApierV1) RemTPTiming(attrs AttrGetTPTiming, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "TimingId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if err := self.StorDb.RemTpData(utils.TBL_TP_TIMINGS, attrs.TPid, attrs.TimingId); err != nil {
return utils.NewErrServerError(err)
} else {
*reply = "OK"
}
return nil
}

98
apier/v1/tpusers.go Normal file
View File

@@ -0,0 +1,98 @@
/*
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 v1
import (
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Creates a new alias within a tariff plan
func (self *ApierV1) SetTPUser(attrs utils.TPUsers, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "Direction", "Tenant", "Category", "Account", "Subject", "Group"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
tm := engine.APItoModelUsers(&attrs)
if err := self.StorDb.SetTpUsers(tm); err != nil {
return utils.NewErrServerError(err)
}
*reply = "OK"
return nil
}
type AttrGetTPUser struct {
TPid string // Tariff plan id
UserId string
}
// Queries specific User on Tariff plan
func (self *ApierV1) GetTPUser(attr AttrGetTPUser, reply *utils.TPUsers) error {
if missing := utils.MissingStructFields(&attr, []string{"TPid"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
usr := &engine.TpUser{
Tpid: attr.TPid,
}
usr.SetId(attr.UserId)
if tms, err := self.StorDb.GetTpUsers(usr); err != nil {
return utils.NewErrServerError(err)
} else if len(tms) == 0 {
return utils.ErrNotFound
} else {
tmMap, err := engine.TpUsers(tms).GetUsers()
if err != nil {
return err
}
*reply = *tmMap[usr.GetId()]
}
return nil
}
type AttrGetTPUserIds struct {
TPid string // Tariff plan id
utils.Paginator
}
// Queries alias identities on specific tariff plan.
func (self *ApierV1) GetTPUserIds(attrs AttrGetTPUserIds, reply *[]string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBL_TP_USERS, utils.TPDistinctIds{"tenant", "user_name"}, nil, &attrs.Paginator); err != nil {
return utils.NewErrServerError(err)
} else if ids == nil {
return utils.ErrNotFound
} else {
*reply = ids
}
return nil
}
// Removes specific User on Tariff plan
func (self *ApierV1) RemTPUser(attrs AttrGetTPUser, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "UserId"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if err := self.StorDb.RemTpData(utils.TBL_TP_USERS, attrs.TPid); err != nil {
return utils.NewErrServerError(err)
} else {
*reply = "OK"
}
return nil
}

View File

@@ -0,0 +1,526 @@
/*
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 apier
import (
"flag"
"fmt"
"net/rpc/jsonrpc"
"os"
"os/exec"
"path"
"reflect"
"testing"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
var fsjsonCfgPath string
var fsjsonCfg *config.CGRConfig
var waitFs = flag.Int("wait_fs", 500, "Number of miliseconds to wait for FreeSWITCH to start")
func init() {
fsjsonCfgPath = path.Join(*dataDir, "tutorials", "fs_json", "cgrates", "etc", "cgrates", "cgrates.cfg")
fsjsonCfg, _ = config.NewCGRConfigFromFile(&fsjsonCfgPath)
}
// Remove here so they can be properly created by init script
func TestFsJsonRemoveDirs(t *testing.T) {
if !*testLocal {
return
}
for _, pathDir := range []string{cfg.CdreDefaultInstance.ExportDir, fsjsonCfg.HistoryDir} {
if err := os.RemoveAll(pathDir); err != nil {
t.Fatal("Error removing folder: ", pathDir, err)
}
}
}
// Empty tables before using them
func TestFsJsonCreateTables(t *testing.T) {
if !*testLocal {
return
}
if *storDbType != utils.MYSQL {
t.Fatal("Unsupported storDbType")
}
var mysql *engine.MySQLStorage
if d, err := engine.NewMySQLStorage(fsjsonCfg.StorDBHost, fsjsonCfg.StorDBPort, fsjsonCfg.StorDBName, fsjsonCfg.StorDBUser, fsjsonCfg.StorDBPass); err != nil {
t.Fatal("Error on opening database connection: ", err)
} else {
mysql = d.(*engine.MySQLStorage)
}
if err := mysql.CreateTablesFromScript(path.Join(*dataDir, "storage", *storDbType, engine.CREATE_CDRS_TABLES_SQL)); err != nil {
t.Fatal("Error on mysql creation: ", err.Error())
return // No point in going further
}
for _, tbl := range []string{utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_EXTRA} {
if _, err := mysql.Db.Query(fmt.Sprintf("SELECT 1 from %s", tbl)); err != nil {
t.Fatal(err.Error())
}
}
}
func TestFsJsonInitDataDb(t *testing.T) {
if !*testLocal {
return
}
ratingDb, err := engine.ConfigureRatingStorage(fsjsonCfg.RatingDBType, fsjsonCfg.RatingDBHost, fsjsonCfg.RatingDBPort, fsjsonCfg.RatingDBName, fsjsonCfg.RatingDBUser, fsjsonCfg.RatingDBPass, fsjsonCfg.DBDataEncoding)
if err != nil {
t.Fatal("Cannot connect to dataDb", err)
}
accountDb, err := engine.ConfigureAccountingStorage(fsjsonCfg.AccountDBType, fsjsonCfg.AccountDBHost, fsjsonCfg.AccountDBPort, fsjsonCfg.AccountDBName,
fsjsonCfg.AccountDBUser, fsjsonCfg.AccountDBPass, fsjsonCfg.DBDataEncoding)
if err != nil {
t.Fatal("Cannot connect to dataDb", err)
}
for _, db := range []engine.Storage{ratingDb, accountDb} {
if err := db.Flush(); err != nil {
t.Fatal("Cannot reset dataDb", err)
}
}
}
func TestFsJsonStartFs(t *testing.T) {
if !*testLocal {
return
}
exec.Command("pkill", "freeswitch").Run() // Just to make sure another one is not running, bit brutal maybe we can fine tune it
go func() {
fs := exec.Command("/usr/share/cgrates/tutorials/fs_json/freeswitch/etc/init.d/freeswitch", "start")
out, _ := fs.CombinedOutput()
engine.Logger.Info(fmt.Sprintf("CgrEngine-TestFsJson: %s", out))
}()
time.Sleep(time.Duration(*waitFs) * time.Millisecond) // Give time to rater to fire up
}
// Finds cgr-engine executable and starts it with default configuration
func TestFsJsonStartEngine(t *testing.T) {
if !*testLocal {
return
}
exec.Command("pkill", "cgr-engine").Run() // Just to make sure another one is not running, bit brutal maybe we can fine tune it
go func() {
eng := exec.Command("/usr/share/cgrates/tutorials/fs_json/cgrates/etc/init.d/cgrates", "start")
out, _ := eng.CombinedOutput()
engine.Logger.Info(fmt.Sprintf("CgrEngine-TestFsJson: %s", out))
}()
time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time to rater to fire up
}
// Connect rpc client to rater
func TestFsJsonRpcConn(t *testing.T) {
if !*testLocal {
return
}
var err error
rater, err = jsonrpc.Dial("tcp", fsjsonCfg.RPCJSONListen)
if err != nil {
t.Fatal("Could not connect to rater: ", err.Error())
}
}
// Make sure we start with fresh data
func TestFsJsonEmptyCache(t *testing.T) {
if !*testLocal {
return
}
var rcvStats *utils.CacheStats
expectedStats := &utils.CacheStats{Destinations: 0, RatingPlans: 0, RatingProfiles: 0, Actions: 0}
var args utils.AttrCacheStats
if err := rater.Call("ApierV1.GetCacheStats", args, &rcvStats); err != nil {
t.Error("Got error on ApierV1.GetCacheStats: ", err.Error())
} else if !reflect.DeepEqual(expectedStats, rcvStats) {
t.Errorf("Calling ApierV1.GetCacheStats expected: %v, received: %v", expectedStats, rcvStats)
}
}
func TestFsJsonLoadTariffPlans(t *testing.T) {
if !*testLocal {
return
}
reply := ""
// Simple test that command is executed without errors
attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tutorials", "fs_json", "cgrates", "tariffplans")}
if err := rater.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil {
t.Error("Got error on ApierV1.LoadTariffPlanFromFolder: ", err.Error())
} else if reply != "OK" {
t.Error("Calling ApierV1.LoadTariffPlanFromFolder got reply: ", reply)
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups
var rcvStats *utils.CacheStats
expectedStats := &utils.CacheStats{Destinations: 3, RatingPlans: 3, RatingProfiles: 3, Actions: 5, SharedGroups: 1, RatingAliases: 1, AccountAliases: 1, DerivedChargers: 1}
var args utils.AttrCacheStats
if err := rater.Call("ApierV1.GetCacheStats", args, &rcvStats); err != nil {
t.Error("Got error on ApierV1.GetCacheStats: ", err.Error())
} else if !reflect.DeepEqual(expectedStats, rcvStats) {
t.Errorf("Calling ApierV1.GetCacheStats expected: %v, received: %v", expectedStats, rcvStats)
}
}
func TestFsJsonGetAccount1001(t *testing.T) {
if !*testLocal {
return
}
var acnt *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001", Direction: "*out"}
if err := rater.Call("ApierV1.GetAccount", attrs, &acnt); err != nil {
t.Error("Got error on ApierV1.GetAccount: ", err.Error())
}
if acnt.BalanceMap[engine.CREDIT+attrs.Direction].GetTotalValue() != 10.0 {
t.Errorf("Calling ApierV1.GetBalance expected: 10.0, received: %f", acnt.BalanceMap[engine.CREDIT+attrs.Direction].GetTotalValue())
}
if len(acnt.BalanceMap[engine.CREDIT+attrs.Direction]) != 2 {
t.Errorf("Unexpected number of balances found: %d", len(acnt.BalanceMap[engine.CREDIT+attrs.Direction]))
}
blncLst := acnt.BalanceMap[engine.CREDIT+attrs.Direction]
for _, blnc := range blncLst {
if len(blnc.SharedGroup) == 0 && blnc.Value != 5 {
t.Errorf("Unexpected value for general balance: %f", blnc.Value)
} else if blnc.SharedGroup == "SHARED_A" && blnc.Value != 5 {
t.Errorf("Unexpected value for shared balance: %f", blnc.Value)
}
}
}
func TestFsJsonGetAccount1002(t *testing.T) {
if !*testLocal {
return
}
var acnt *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1002", Direction: "*out"}
if err := rater.Call("ApierV1.GetAccount", attrs, &acnt); err != nil {
t.Error("Got error on ApierV1.GetAccount: ", err.Error())
}
if acnt.BalanceMap[engine.CREDIT+attrs.Direction].GetTotalValue() != 10.0 {
t.Errorf("Calling ApierV1.GetBalance expected: 10.0, received: %f", acnt.BalanceMap[engine.CREDIT+attrs.Direction].GetTotalValue())
}
if len(acnt.BalanceMap[engine.CREDIT+attrs.Direction]) != 1 {
t.Errorf("Unexpected number of balances found: %d", len(acnt.BalanceMap[engine.CREDIT+attrs.Direction]))
}
blnc := acnt.BalanceMap[engine.CREDIT+attrs.Direction][0]
if blnc.Value != 10 {
t.Errorf("Unexpected value for general balance: %f", blnc.Value)
}
}
func TestFsJsonGetAccount1003(t *testing.T) {
if !*testLocal {
return
}
var acnt *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1003", Direction: "*out"}
if err := rater.Call("ApierV1.GetAccount", attrs, &acnt); err != nil {
t.Error("Got error on ApierV1.GetAccount: ", err.Error())
}
if acnt.BalanceMap[engine.CREDIT+attrs.Direction].GetTotalValue() != 10.0 {
t.Errorf("Calling ApierV1.GetBalance expected: 10.0, received: %f", acnt.BalanceMap[engine.CREDIT+attrs.Direction].GetTotalValue())
}
if len(acnt.BalanceMap[engine.CREDIT+attrs.Direction]) != 1 {
t.Errorf("Unexpected number of balances found: %d", len(acnt.BalanceMap[engine.CREDIT+attrs.Direction]))
}
blnc := acnt.BalanceMap[engine.CREDIT+attrs.Direction][0]
if blnc.Value != 10 {
t.Errorf("Unexpected value for general balance: %f", blnc.Value)
}
}
func TestFsJsonGetAccount1004(t *testing.T) {
if !*testLocal {
return
}
var acnt *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1004", Direction: "*out"}
if err := rater.Call("ApierV1.GetAccount", attrs, &acnt); err != nil {
t.Error("Got error on ApierV1.GetAccount: ", err.Error())
}
if acnt.BalanceMap[engine.CREDIT+attrs.Direction].GetTotalValue() != 10.0 {
t.Errorf("Calling ApierV1.GetBalance expected: 10.0, received: %f", acnt.BalanceMap[engine.CREDIT+attrs.Direction].GetTotalValue())
}
if len(acnt.BalanceMap[engine.CREDIT+attrs.Direction]) != 1 {
t.Errorf("Unexpected number of balances found: %d", len(acnt.BalanceMap[engine.CREDIT+attrs.Direction]))
}
blnc := acnt.BalanceMap[engine.CREDIT+attrs.Direction][0]
if blnc.Value != 10 {
t.Errorf("Unexpected value for general balance: %f", blnc.Value)
}
}
func TestFsJsonGetAccount1006(t *testing.T) {
if !*testLocal {
return
}
var acnt *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1006", Direction: "*out"}
if err := rater.Call("ApierV1.GetAccount", attrs, &acnt); err == nil {
t.Error("Got no error when querying unexisting balance")
}
}
func TestFsJsonGetAccount1007(t *testing.T) {
if !*testLocal {
return
}
var acnt *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1007", Direction: "*out"}
if err := rater.Call("ApierV1.GetAccount", attrs, &acnt); err != nil {
t.Error("Got error on ApierV1.GetAccount: ", err.Error())
}
if acnt.BalanceMap[engine.CREDIT+attrs.Direction].GetTotalValue() != 0 {
t.Errorf("Calling ApierV1.GetBalance expected: 0, received: %f", acnt.BalanceMap[engine.CREDIT+attrs.Direction].GetTotalValue())
}
if len(acnt.BalanceMap[engine.CREDIT+attrs.Direction]) != 1 {
t.Errorf("Unexpected number of balances found: %d", len(acnt.BalanceMap[engine.CREDIT+attrs.Direction]))
}
blncLst := acnt.BalanceMap[engine.CREDIT+attrs.Direction]
for _, blnc := range blncLst {
if len(blnc.SharedGroup) == 0 && blnc.Value != 0 { // General balance
t.Errorf("Unexpected value for general balance: %f", blnc.Value)
} else if blnc.SharedGroup == "SHARED_A" && blnc.Value != 0 {
t.Errorf("Unexpected value for shared balance: %f", blnc.Value)
}
}
}
func TestMaxCallDuration(t *testing.T) {
if !*testLocal {
return
}
cd := engine.CallDescriptor{
Direction: "*out",
Tenant: "cgrates.org",
Category: "call",
Subject: "1001",
Account: "1001",
Destination: "1002",
TimeStart: time.Date(2014, 3, 27, 10, 42, 26, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 27, 10, 42, 26, 0, time.UTC).Add(fsjsonCfg.SMMaxCallDuration),
}
var remainingDurationFloat float64
if err := rater.Call("Responder.GetMaxSessionTime", cd, &remainingDurationFloat); err != nil {
t.Error(err)
} else {
remainingDuration := time.Duration(remainingDurationFloat)
if remainingDuration < time.Duration(90)*time.Minute {
t.Errorf("Expecting maxSessionTime around 1h30m, received as: %v", remainingDuration)
}
}
cd = engine.CallDescriptor{
Direction: "*out",
Tenant: "cgrates.org",
Category: "call",
Subject: "1002",
Account: "1002",
Destination: "1001",
TimeStart: time.Date(2014, 3, 27, 10, 42, 26, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 27, 10, 42, 26, 0, time.UTC).Add(fsjsonCfg.SMMaxCallDuration),
}
if err := rater.Call("Responder.GetMaxSessionTime", cd, &remainingDurationFloat); err != nil {
t.Error(err)
} else {
remainingDuration := time.Duration(remainingDurationFloat)
if remainingDuration < time.Duration(45)*time.Minute {
t.Errorf("Expecting maxSessionTime around 45m, received as: %v", remainingDuration)
}
}
cd = engine.CallDescriptor{
Direction: "*out",
Tenant: "cgrates.org",
Category: "call",
Subject: "1006",
Account: "1006",
Destination: "1001",
TimeStart: time.Date(2014, 3, 27, 10, 42, 26, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 27, 10, 42, 26, 0, time.UTC).Add(fsjsonCfg.SMMaxCallDuration),
}
if err := rater.Call("Responder.GetMaxSessionTime", cd, &remainingDurationFloat); err != nil {
t.Error(err)
} else {
remainingDuration := time.Duration(remainingDurationFloat)
if remainingDuration < time.Duration(45)*time.Minute {
t.Errorf("Expecting maxSessionTime around 45m, received as: %v", remainingDuration)
}
}
// 1007 should use the 1001 balance when doing maxSessionTime
cd = engine.CallDescriptor{
Direction: "*out",
Tenant: "cgrates.org",
Category: "call",
Subject: "1007",
Account: "1007",
Destination: "1001",
TimeStart: time.Date(2014, 3, 27, 10, 42, 26, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 27, 10, 42, 26, 0, time.UTC).Add(fsjsonCfg.SMMaxCallDuration),
}
if err := rater.Call("Responder.GetMaxSessionTime", cd, &remainingDurationFloat); err != nil {
t.Error(err)
} else {
remainingDuration := time.Duration(remainingDurationFloat)
if remainingDuration < time.Duration(20)*time.Minute {
t.Errorf("Expecting maxSessionTime around 20m, received as: %v", remainingDuration)
}
}
}
func TestMaxDebit1001(t *testing.T) {
if !*testLocal {
return
}
cc := &engine.CallCost{}
var acnt *engine.Account
cd := engine.CallDescriptor{
Direction: "*out",
Tenant: "cgrates.org",
Category: "call",
Subject: "1001",
Account: "1001",
Destination: "1002",
TimeStart: time.Date(2014, 3, 27, 10, 42, 26, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 27, 10, 42, 26, 0, time.UTC).Add(time.Duration(10) * time.Second),
}
if err := rater.Call("Responder.MaxDebit", cd, cc); err != nil {
t.Error(err.Error())
} else if cc.GetDuration() > time.Duration(1)*time.Minute {
t.Errorf("Unexpected call duration received: %v", cc.GetDuration())
}
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001", Direction: "*out"}
if err := rater.Call("ApierV1.GetAccount", attrs, &acnt); err != nil {
t.Error("Got error on ApierV1.GetAccount: ", err.Error())
} else {
if len(acnt.BalanceMap["*monetary*out"]) != 2 {
t.Errorf("Unexpected number of balances found: %d", len(acnt.BalanceMap["*monetary*out"]))
}
blncLst := acnt.BalanceMap["*monetary*out"]
for _, blnc := range blncLst {
if blnc.SharedGroup == "SHARED_A" && blnc.Value != 5 {
t.Errorf("Unexpected value for shared balance: %f", blnc.Value)
} else if len(blnc.SharedGroup) == 0 && blnc.Value != 4.4 {
t.Errorf("Unexpected value for general balance: %f", blnc.Value)
}
}
}
}
func TestMaxDebit1007(t *testing.T) {
if !*testLocal {
return
}
cc := &engine.CallCost{}
var acnt *engine.Account
cd := engine.CallDescriptor{
Direction: "*out",
Tenant: "cgrates.org",
Category: "call",
Subject: "1007",
Account: "1007",
Destination: "1002",
TimeStart: time.Date(2014, 3, 27, 10, 42, 26, 0, time.UTC),
TimeEnd: time.Date(2014, 3, 27, 10, 42, 26, 0, time.UTC).Add(time.Duration(10) * time.Second),
}
if err := rater.Call("Responder.MaxDebit", cd, cc); err != nil {
t.Error(err.Error())
} else if cc.GetDuration() > time.Duration(1)*time.Minute {
t.Errorf("Unexpected call duration received: %v", cc.GetDuration())
}
// Debit out of shared balance should reflect in the 1001 instead of 1007
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001", Direction: "*out"}
if err := rater.Call("ApierV1.GetAccount", attrs, &acnt); err != nil {
t.Error("Got error on ApierV1.GetAccount: ", err.Error())
} else {
if len(acnt.BalanceMap["*monetary*out"]) != 2 {
t.Errorf("Unexpected number of balances found: %d", len(acnt.BalanceMap["*monetary*out"]))
}
blncLst := acnt.BalanceMap["*monetary*out"]
for _, blnc := range blncLst {
if blnc.SharedGroup == "SHARED_A" && blnc.Value != 4 {
t.Errorf("Unexpected value for shared balance: %f", blnc.Value)
} else if len(blnc.SharedGroup) == 0 && blnc.Value != 4.4 {
t.Errorf("Unexpected value for general balance: %f", blnc.Value)
}
}
}
// Make sure 1007 remains the same
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1007", Direction: "*out"}
if err := rater.Call("ApierV1.GetAccount", attrs, &acnt); err != nil {
t.Error("Got error on ApierV1.GetAccount: ", err.Error())
}
if acnt.BalanceMap[engine.CREDIT+attrs.Direction].GetTotalValue() != 0 {
t.Errorf("Calling ApierV1.GetBalance expected: 0, received: %f", acnt.BalanceMap[engine.CREDIT+attrs.Direction].GetTotalValue())
}
if len(acnt.BalanceMap[engine.CREDIT+attrs.Direction]) != 1 {
t.Errorf("Unexpected number of balances found: %d", len(acnt.BalanceMap[engine.CREDIT+attrs.Direction]))
}
blnc := acnt.BalanceMap[engine.CREDIT+attrs.Direction][0]
if len(blnc.SharedGroup) == 0 { // General balance
t.Errorf("Unexpected general balance: %f", blnc.Value)
} else if blnc.SharedGroup == "SHARED_A" && blnc.Value != 0 {
t.Errorf("Unexpected value for shared balance: %f", blnc.Value)
}
}
func TestDerivedChargers1001(t *testing.T) {
if !*testLocal {
return
}
attrs := &utils.AttrDerivedChargers{Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001"}
expectedDCs := utils.DerivedChargers([]*utils.DerivedCharger{
&utils.DerivedCharger{RunId: "fs_json_run", ReqTypeField: "^rated", DirectionField: "*default", TenantField: "*default", CategoryField: "*default",
AccountField: "*default", SubjectField: "^1002", DestinationField: "*default", SetupTimeField: "*default", AnswerTimeField: "*default", UsageField: "*default"},
})
var rcvRspDCs utils.DerivedChargers
if err := rater.Call("Responder.GetDerivedChargers", attrs, &rcvRspDCs); err != nil {
t.Error(err.Error())
} else if !reflect.DeepEqual(expectedDCs, rcvRspDCs) {
t.Errorf("Expected: %v: received: %v", expectedDCs, rcvRspDCs)
}
// Make sure that over Apier we get the same result as over Responder
var rcvApierDCs utils.DerivedChargers
if err := rater.Call("ApierV1.GetDerivedChargers", attrs, &rcvApierDCs); err != nil {
t.Error(err.Error())
} else if !reflect.DeepEqual(rcvRspDCs, rcvApierDCs) {
t.Errorf("Expected: %v: received: %v", rcvRspDCs, rcvApierDCs)
}
}
// Simply kill the engine after we are done with tests within this file
func TestFsJsonStopEngine(t *testing.T) {
if !*testLocal {
return
}
go func() {
eng := exec.Command("/usr/share/cgrates/tutorials/fs_json/cgrates/etc/init.d/cgrates", "stop")
out, _ := eng.CombinedOutput()
engine.Logger.Info(fmt.Sprintf("CgrEngine-TestFsJson: %s", out))
}()
}
func TestFsJsonStopFs(t *testing.T) {
if !*testLocal {
return
}
go func() {
fs := exec.Command("/usr/share/cgrates/tutorials/fs_json/freeswitch/etc/init.d/freeswitch", "stop")
out, _ := fs.CombinedOutput()
engine.Logger.Info(fmt.Sprintf("CgrEngine-TestFsJson: %s", out))
}()
}

258
apier/v2/apier.go Normal file
View File

@@ -0,0 +1,258 @@
/*
Real-time Charging System 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 v2
import (
"errors"
"fmt"
"os"
"path"
"strings"
"github.com/cgrates/cgrates/apier/v1"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
type ApierV2 struct {
v1.ApierV1
}
type AttrLoadRatingProfile struct {
TPid string
RatingProfileId string
}
// Process dependencies and load a specific rating profile from storDb into dataDb.
func (self *ApierV2) LoadRatingProfile(attrs AttrLoadRatingProfile, reply *string) error {
if len(attrs.TPid) == 0 {
return utils.NewErrMandatoryIeMissing("TPid")
}
tpRpf := &utils.TPRatingProfile{TPid: attrs.TPid}
tpRpf.SetRatingProfilesId(attrs.RatingProfileId)
rpf := engine.APItoModelRatingProfile(tpRpf)
dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid, self.Config.DefaultTimezone, self.Config.LoadHistorySize)
if err := dbReader.LoadRatingProfilesFiltered(&rpf[0]); err != nil {
return utils.NewErrServerError(err)
}
//Automatic cache of the newly inserted rating profile
var ratingProfile []string
if tpRpf.KeyId() != ":::" { // if has some filters
ratingProfile = []string{utils.RATING_PROFILE_PREFIX + tpRpf.KeyId()}
}
if err := self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.RATING_PROFILE_PREFIX: ratingProfile}); err != nil {
return err
}
*reply = v1.OK
return nil
}
type AttrLoadAccountActions struct {
TPid string
AccountActionsId string
}
// Process dependencies and load a specific AccountActions profile from storDb into dataDb.
func (self *ApierV2) LoadAccountActions(attrs AttrLoadAccountActions, reply *string) error {
if len(attrs.TPid) == 0 {
return utils.NewErrMandatoryIeMissing("TPid")
}
dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid, self.Config.DefaultTimezone, self.Config.LoadHistorySize)
tpAa := &utils.TPAccountActions{TPid: attrs.TPid}
tpAa.SetAccountActionsId(attrs.AccountActionsId)
aa := engine.APItoModelAccountAction(tpAa)
if _, err := engine.Guardian.Guard(func() (interface{}, error) {
if err := dbReader.LoadAccountActionsFiltered(aa); err != nil {
return 0, err
}
return 0, nil
}, 0, attrs.AccountActionsId); err != nil {
return utils.NewErrServerError(err)
}
// ToDo: Get the action keys loaded by dbReader so we reload only these in cache
// Need to do it before scheduler otherwise actions to run will be unknown
if err := self.RatingDb.CacheRatingPrefixes(utils.DERIVEDCHARGERS_PREFIX, utils.ACTION_PREFIX, utils.SHARED_GROUP_PREFIX); err != nil {
return err
}
if self.Sched != nil {
self.Sched.LoadActionPlans(self.RatingDb)
self.Sched.Restart()
}
*reply = v1.OK
return nil
}
type AttrLoadDerivedChargers struct {
TPid string
DerivedChargersId string
}
// Load derived chargers from storDb into dataDb.
func (self *ApierV2) LoadDerivedChargers(attrs AttrLoadDerivedChargers, reply *string) error {
if len(attrs.TPid) == 0 {
return utils.NewErrMandatoryIeMissing("TPid")
}
tpDc := &utils.TPDerivedChargers{TPid: attrs.TPid}
tpDc.SetDerivedChargersId(attrs.DerivedChargersId)
dc := engine.APItoModelDerivedCharger(tpDc)
dbReader := engine.NewTpReader(self.RatingDb, self.AccountDb, self.StorDb, attrs.TPid, self.Config.DefaultTimezone, self.Config.LoadHistorySize)
if err := dbReader.LoadDerivedChargersFiltered(&dc[0], true); err != nil {
return utils.NewErrServerError(err)
}
//Automatic cache of the newly inserted rating plan
var dcsChanged []string
if len(attrs.DerivedChargersId) != 0 {
dcsChanged = []string{utils.DERIVEDCHARGERS_PREFIX + attrs.DerivedChargersId}
}
if err := self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.DERIVEDCHARGERS_PREFIX: dcsChanged}); err != nil {
return err
}
*reply = v1.OK
return nil
}
func (self *ApierV2) LoadTariffPlanFromFolder(attrs utils.AttrLoadTpFromFolder, reply *engine.LoadInstance) error {
if len(attrs.FolderPath) == 0 {
return fmt.Errorf("%s:%s", utils.ErrMandatoryIeMissing.Error(), "FolderPath")
}
if fi, err := os.Stat(attrs.FolderPath); err != nil {
if strings.HasSuffix(err.Error(), "no such file or directory") {
return utils.ErrInvalidPath
}
return utils.NewErrServerError(err)
} else if !fi.IsDir() {
return utils.ErrInvalidPath
}
loader := engine.NewTpReader(self.RatingDb, self.AccountDb, engine.NewFileCSVStorage(utils.CSV_SEP,
path.Join(attrs.FolderPath, utils.DESTINATIONS_CSV),
path.Join(attrs.FolderPath, utils.TIMINGS_CSV),
path.Join(attrs.FolderPath, utils.RATES_CSV),
path.Join(attrs.FolderPath, utils.DESTINATION_RATES_CSV),
path.Join(attrs.FolderPath, utils.RATING_PLANS_CSV),
path.Join(attrs.FolderPath, utils.RATING_PROFILES_CSV),
path.Join(attrs.FolderPath, utils.SHARED_GROUPS_CSV),
path.Join(attrs.FolderPath, utils.LCRS_CSV),
path.Join(attrs.FolderPath, utils.ACTIONS_CSV),
path.Join(attrs.FolderPath, utils.ACTION_PLANS_CSV),
path.Join(attrs.FolderPath, utils.ACTION_TRIGGERS_CSV),
path.Join(attrs.FolderPath, utils.ACCOUNT_ACTIONS_CSV),
path.Join(attrs.FolderPath, utils.DERIVED_CHARGERS_CSV),
path.Join(attrs.FolderPath, utils.CDR_STATS_CSV),
path.Join(attrs.FolderPath, utils.USERS_CSV),
path.Join(attrs.FolderPath, utils.ALIASES_CSV),
), "", self.Config.DefaultTimezone, self.Config.LoadHistorySize)
if err := loader.LoadAll(); err != nil {
return utils.NewErrServerError(err)
}
if attrs.DryRun {
*reply = engine.LoadInstance{LoadId: utils.DRYRUN}
return nil // Mission complete, no errors
}
if attrs.Validate {
if !loader.IsValid() {
return errors.New("invalid data")
}
}
if err := loader.WriteToDatabase(attrs.FlushDb, false); err != nil {
return utils.NewErrServerError(err)
}
// Make sure the items are in the cache
dstIds, _ := loader.GetLoadedIds(utils.DESTINATION_PREFIX)
dstKeys := make([]string, len(dstIds))
for idx, dId := range dstIds {
dstKeys[idx] = utils.DESTINATION_PREFIX + dId // Cache expects them as redis keys
}
rplIds, _ := loader.GetLoadedIds(utils.RATING_PLAN_PREFIX)
rpKeys := make([]string, len(rplIds))
for idx, rpId := range rplIds {
rpKeys[idx] = utils.RATING_PLAN_PREFIX + rpId
}
rpfIds, _ := loader.GetLoadedIds(utils.RATING_PROFILE_PREFIX)
rpfKeys := make([]string, len(rpfIds))
for idx, rpfId := range rpfIds {
rpfKeys[idx] = utils.RATING_PROFILE_PREFIX + rpfId
}
actIds, _ := loader.GetLoadedIds(utils.ACTION_PREFIX)
actKeys := make([]string, len(actIds))
for idx, actId := range actIds {
actKeys[idx] = utils.ACTION_PREFIX + actId
}
shgIds, _ := loader.GetLoadedIds(utils.SHARED_GROUP_PREFIX)
shgKeys := make([]string, len(shgIds))
for idx, shgId := range shgIds {
shgKeys[idx] = utils.SHARED_GROUP_PREFIX + shgId
}
aliases, _ := loader.GetLoadedIds(utils.ALIASES_PREFIX)
alsKeys := make([]string, len(aliases))
for idx, alias := range aliases {
alsKeys[idx] = utils.ALIASES_PREFIX + alias
}
lcrIds, _ := loader.GetLoadedIds(utils.LCR_PREFIX)
lcrKeys := make([]string, len(lcrIds))
for idx, lcrId := range lcrIds {
lcrKeys[idx] = utils.LCR_PREFIX + lcrId
}
dcs, _ := loader.GetLoadedIds(utils.DERIVEDCHARGERS_PREFIX)
dcsKeys := make([]string, len(dcs))
for idx, dc := range dcs {
dcsKeys[idx] = utils.DERIVEDCHARGERS_PREFIX + dc
}
aps, _ := loader.GetLoadedIds(utils.ACTION_TIMING_PREFIX)
engine.Logger.Info("ApierV1.LoadTariffPlanFromFolder, reloading cache.")
if err := self.RatingDb.CacheRatingPrefixValues(map[string][]string{
utils.DESTINATION_PREFIX: dstKeys,
utils.RATING_PLAN_PREFIX: rpKeys,
utils.RATING_PROFILE_PREFIX: rpfKeys,
utils.LCR_PREFIX: lcrKeys,
utils.DERIVEDCHARGERS_PREFIX: dcsKeys,
utils.ACTION_PREFIX: actKeys,
utils.SHARED_GROUP_PREFIX: shgKeys,
}); err != nil {
return err
}
if err := self.AccountDb.CacheAccountingPrefixValues(map[string][]string{
utils.ALIASES_PREFIX: alsKeys,
}); err != nil {
return err
}
if len(aps) != 0 && self.Sched != nil {
engine.Logger.Info("ApierV1.LoadTariffPlanFromFolder, reloading scheduler.")
self.Sched.LoadActionPlans(self.RatingDb)
self.Sched.Restart()
}
cstKeys, _ := loader.GetLoadedIds(utils.CDR_STATS_PREFIX)
if len(cstKeys) != 0 && self.CdrStatsSrv != nil {
if err := self.CdrStatsSrv.ReloadQueues(cstKeys, nil); err != nil {
return err
}
}
userKeys, _ := loader.GetLoadedIds(utils.USERS_PREFIX)
if len(userKeys) != 0 && self.Users != nil {
var r string
if err := self.Users.ReloadUsers("", &r); err != nil {
return err
}
}
*reply = *loader.GetLoadInstance()
return nil
}

136
apier/v2/cdre.go Normal file
View File

@@ -0,0 +1,136 @@
/*
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 v2
import (
"fmt"
"path"
"strconv"
"strings"
"time"
"unicode/utf8"
"github.com/cgrates/cgrates/cdre"
"github.com/cgrates/cgrates/utils"
)
// Export Cdrs to file
func (self *ApierV2) ExportCdrsToFile(attr utils.AttrExportCdrsToFile, reply *utils.ExportedFileCdrs) error {
var err error
cdreReloadStruct := <-self.Config.ConfigReloads[utils.CDRE] // Read the content of the channel, locking it
defer func() { self.Config.ConfigReloads[utils.CDRE] <- cdreReloadStruct }() // Unlock reloads at exit
exportTemplate := self.Config.CdreProfiles[utils.META_DEFAULT]
if attr.ExportTemplate != nil && len(*attr.ExportTemplate) != 0 { // Export template prefered, use it
var hasIt bool
if exportTemplate, hasIt = self.Config.CdreProfiles[*attr.ExportTemplate]; !hasIt {
return fmt.Errorf("%s:ExportTemplate", utils.ErrNotFound)
}
}
cdrFormat := exportTemplate.CdrFormat
if attr.CdrFormat != nil && len(*attr.CdrFormat) != 0 {
cdrFormat = strings.ToLower(*attr.CdrFormat)
}
if !utils.IsSliceMember(utils.CdreCdrFormats, cdrFormat) {
return utils.NewErrMandatoryIeMissing("CdrFormat")
}
fieldSep := exportTemplate.FieldSeparator
if attr.FieldSeparator != nil && len(*attr.FieldSeparator) != 0 {
fieldSep, _ = utf8.DecodeRuneInString(*attr.FieldSeparator)
if fieldSep == utf8.RuneError {
return fmt.Errorf("%s:FieldSeparator:%s", utils.ErrServerError, "Invalid")
}
}
exportDir := exportTemplate.ExportDir
if attr.ExportDir != nil && len(*attr.ExportDir) != 0 {
exportDir = *attr.ExportDir
}
exportId := strconv.FormatInt(time.Now().Unix(), 10)
if attr.ExportId != nil && len(*attr.ExportId) != 0 {
exportId = *attr.ExportId
}
fileName := fmt.Sprintf("cdre_%s.%s", exportId, cdrFormat)
if attr.ExportFileName != nil && len(*attr.ExportFileName) != 0 {
fileName = *attr.ExportFileName
}
filePath := path.Join(exportDir, fileName)
if cdrFormat == utils.DRYRUN {
filePath = utils.DRYRUN
}
dataUsageMultiplyFactor := exportTemplate.DataUsageMultiplyFactor
if attr.DataUsageMultiplyFactor != nil && *attr.DataUsageMultiplyFactor != 0.0 {
dataUsageMultiplyFactor = *attr.DataUsageMultiplyFactor
}
smsUsageMultiplyFactor := exportTemplate.SmsUsageMultiplyFactor
if attr.SmsUsageMultiplyFactor != nil && *attr.SmsUsageMultiplyFactor != 0.0 {
smsUsageMultiplyFactor = *attr.SmsUsageMultiplyFactor
}
genericUsageMultiplyFactor := exportTemplate.GenericUsageMultiplyFactor
if attr.GenericUsageMultiplyFactor != nil && *attr.GenericUsageMultiplyFactor != 0.0 {
genericUsageMultiplyFactor = *attr.GenericUsageMultiplyFactor
}
costMultiplyFactor := exportTemplate.CostMultiplyFactor
if attr.CostMultiplyFactor != nil && *attr.CostMultiplyFactor != 0.0 {
costMultiplyFactor = *attr.CostMultiplyFactor
}
costShiftDigits := exportTemplate.CostShiftDigits
if attr.CostShiftDigits != nil {
costShiftDigits = *attr.CostShiftDigits
}
roundingDecimals := exportTemplate.CostRoundingDecimals
if attr.RoundDecimals != nil {
roundingDecimals = *attr.RoundDecimals
}
maskDestId := exportTemplate.MaskDestId
if attr.MaskDestinationId != nil && len(*attr.MaskDestinationId) != 0 {
maskDestId = *attr.MaskDestinationId
}
maskLen := exportTemplate.MaskLength
if attr.MaskLength != nil {
maskLen = *attr.MaskLength
}
cdrsFltr, err := attr.RpcCdrsFilter.AsCdrsFilter(self.Config.DefaultTimezone)
if err != nil {
return utils.NewErrServerError(err)
}
cdrs, _, err := self.CdrDb.GetStoredCdrs(cdrsFltr)
if err != nil {
return err
} else if len(cdrs) == 0 {
*reply = utils.ExportedFileCdrs{ExportedFilePath: ""}
return nil
}
cdrexp, err := cdre.NewCdrExporter(cdrs, self.CdrDb, exportTemplate, cdrFormat, fieldSep, exportId, dataUsageMultiplyFactor, smsUsageMultiplyFactor, genericUsageMultiplyFactor,
costMultiplyFactor, costShiftDigits, roundingDecimals, self.Config.RoundingDecimals, maskDestId, maskLen, self.Config.HttpSkipTlsVerify, self.Config.DefaultTimezone)
if err != nil {
return utils.NewErrServerError(err)
}
if cdrexp.TotalExportedCdrs() == 0 {
*reply = utils.ExportedFileCdrs{ExportedFilePath: ""}
return nil
}
if err := cdrexp.WriteToFile(filePath); err != nil {
return utils.NewErrServerError(err)
}
*reply = utils.ExportedFileCdrs{ExportedFilePath: filePath, TotalRecords: len(cdrs), TotalCost: cdrexp.TotalCost(), FirstOrderId: cdrexp.FirstOrderId(), LastOrderId: cdrexp.LastOrderId()}
if !attr.SuppressCgrIds {
reply.ExportedCgrIds = cdrexp.PositiveExports()
reply.UnexportedCgrIds = cdrexp.NegativeExports()
}
return nil
}

63
apier/v2/cdrs.go Normal file
View File

@@ -0,0 +1,63 @@
/*
Real-time Charging System 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 v2
import (
"github.com/cgrates/cgrates/apier/v1"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
// Retrieves CDRs based on the filters
func (apier *ApierV2) GetCdrs(attrs utils.RpcCdrsFilter, reply *[]*engine.ExternalCdr) error {
cdrsFltr, err := attrs.AsCdrsFilter(apier.Config.DefaultTimezone)
if err != nil {
return utils.NewErrServerError(err)
}
if cdrs, _, err := apier.CdrDb.GetStoredCdrs(cdrsFltr); err != nil {
return utils.NewErrServerError(err)
} else if len(cdrs) == 0 {
*reply = make([]*engine.ExternalCdr, 0)
} else {
for _, cdr := range cdrs {
*reply = append(*reply, cdr.AsExternalCdr())
}
}
return nil
}
func (apier *ApierV2) CountCdrs(attrs utils.RpcCdrsFilter, reply *int64) error {
cdrsFltr, err := attrs.AsCdrsFilter(apier.Config.DefaultTimezone)
if err != nil {
return utils.NewErrServerError(err)
}
cdrsFltr.Count = true
if _, count, err := apier.CdrDb.GetStoredCdrs(cdrsFltr); err != nil {
return utils.NewErrServerError(err)
} else {
*reply = count
}
return nil
}
// Receive CDRs via RPC methods, not included with APIer because it has way less dependencies and can be standalone
type CdrsV2 struct {
v1.CdrsV1
}

View File

@@ -0,0 +1,250 @@
/*
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 v2
import (
"flag"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
"net/rpc"
"net/rpc/jsonrpc"
"path"
"testing"
"time"
)
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 waitRater = flag.Int("wait_rater", 500, "Number of miliseconds to wait for rater to start and cache")
var cdrsCfgPath string
var cdrsCfg *config.CGRConfig
var cdrsRpc *rpc.Client
func TestV2CdrsMysqlInitConfig(t *testing.T) {
if !*testLocal {
return
}
var err error
cdrsCfgPath = path.Join(*dataDir, "conf", "samples", "cdrsv2mysql")
if cdrsCfg, err = config.NewCGRConfigFromFolder(cdrsCfgPath); err != nil {
t.Fatal("Got config error: ", err.Error())
}
}
func TestV2CdrsMysqlInitDataDb(t *testing.T) {
if !*testLocal {
return
}
if err := engine.InitDataDb(cdrsCfg); err != nil {
t.Fatal(err)
}
}
// InitDb so we can rely on count
func TestV2CdrsMysqlInitCdrDb(t *testing.T) {
if !*testLocal {
return
}
if err := engine.InitStorDb(cdrsCfg); err != nil {
t.Fatal(err)
}
}
func TestV2CdrsMysqlInjectUnratedCdr(t *testing.T) {
if !*testLocal {
return
}
var mysqlDb *engine.MySQLStorage
if d, err := engine.NewMySQLStorage(cdrsCfg.StorDBHost, cdrsCfg.StorDBPort, cdrsCfg.StorDBName, cdrsCfg.StorDBUser, cdrsCfg.StorDBPass,
cdrsCfg.StorDBMaxOpenConns, cdrsCfg.StorDBMaxIdleConns); err != nil {
t.Error("Error on opening database connection: ", err)
return
} else {
mysqlDb = d.(*engine.MySQLStorage)
}
strCdr1 := &engine.StoredCdr{CgrId: utils.Sha1("bbb1", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()),
TOR: utils.VOICE, AccId: "bbb1", CdrHost: "192.168.1.1", CdrSource: "UNKNOWN", ReqType: utils.META_RATED,
Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC),
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
MediationRunId: utils.DEFAULT_RUNID, Cost: 1.201}
if err := mysqlDb.SetCdr(strCdr1); err != nil {
t.Error(err.Error())
}
}
func TestV2CdrsMysqlStartEngine(t *testing.T) {
if !*testLocal {
return
}
if _, err := engine.StopStartEngine(cdrsCfgPath, *waitRater); err != nil {
t.Fatal(err)
}
}
// Connect rpc client to rater
func TestV2CdrsMysqlRpcConn(t *testing.T) {
if !*testLocal {
return
}
var err error
cdrsRpc, err = jsonrpc.Dial("tcp", cdrsCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
if err != nil {
t.Fatal("Could not connect to rater: ", err.Error())
}
}
// Insert some CDRs
func TestV2CdrsMysqlProcessCdr(t *testing.T) {
if !*testLocal {
return
}
var reply string
cdrs := []*engine.StoredCdr{
&engine.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
RatedAccount: "dan", RatedSubject: "dans", Rated: true,
},
&engine.StoredCdr{CgrId: utils.Sha1("abcdeftg", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1002", Subject: "1002", Destination: "1002",
SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
RatedAccount: "dan", RatedSubject: "dans",
},
&engine.StoredCdr{CgrId: utils.Sha1("aererfddf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1003", Subject: "1003", Destination: "1002",
SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
RatedAccount: "dan", RatedSubject: "dans",
},
}
for _, cdr := range cdrs {
if err := cdrsRpc.Call("CdrsV2.ProcessCdr", cdr, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if reply != utils.OK {
t.Error("Unexpected reply received: ", reply)
}
}
}
func TestV2CdrsMysqlGetCdrs(t *testing.T) {
if !*testLocal {
return
}
var reply []*engine.ExternalCdr
req := utils.RpcCdrsFilter{}
if err := cdrsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 4 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
}
// CDRs with errors
req = utils.RpcCdrsFilter{MinCost: utils.Float64Pointer(-1.0), MaxCost: utils.Float64Pointer(0.0)}
if err := cdrsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 2 {
t.Error("Unexpected number of CDRs returned: ", reply)
}
// CDRs Rated
req = utils.RpcCdrsFilter{MinCost: utils.Float64Pointer(-1.0)}
if err := cdrsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 3 {
t.Error("Unexpected number of CDRs returned: ", reply)
}
// CDRs non rated OR SkipRated
req = utils.RpcCdrsFilter{MaxCost: utils.Float64Pointer(-1.0)}
if err := cdrsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", reply)
}
// Skip Errors
req = utils.RpcCdrsFilter{MinCost: utils.Float64Pointer(0.0), MaxCost: utils.Float64Pointer(-1.0)}
if err := cdrsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 2 {
t.Error("Unexpected number of CDRs returned: ", reply)
}
}
func TestV2CdrsMysqlCountCdrs(t *testing.T) {
if !*testLocal {
return
}
var reply int64
req := utils.AttrGetCdrs{}
if err := cdrsRpc.Call("ApierV2.CountCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if reply != 4 {
t.Error("Unexpected number of CDRs returned: ", reply)
}
}
// Test Prepaid CDRs without previous costs being calculated
func TestV2CdrsMysqlProcessPrepaidCdr(t *testing.T) {
if !*testLocal {
return
}
var reply string
cdrs := []*engine.StoredCdr{
&engine.StoredCdr{CgrId: utils.Sha1("dsafdsaf2", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: utils.META_PREPAID, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
RatedAccount: "dan", RatedSubject: "dans", Rated: true,
},
&engine.StoredCdr{CgrId: utils.Sha1("abcdeftg2", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: utils.META_PREPAID, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1002", Subject: "1002", Destination: "1002",
SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
RatedAccount: "dan", RatedSubject: "dans",
},
&engine.StoredCdr{CgrId: utils.Sha1("aererfddf2", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: utils.META_PREPAID, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1003", Subject: "1003", Destination: "1002",
SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
RatedAccount: "dan", RatedSubject: "dans",
},
}
tStart := time.Now()
for _, cdr := range cdrs {
if err := cdrsRpc.Call("CdrsV2.ProcessCdr", cdr, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if reply != utils.OK {
t.Error("Unexpected reply received: ", reply)
}
}
if processDur := time.Now().Sub(tStart); processDur > 1*time.Second {
t.Error("Unexpected processing time", processDur)
}
}
func TestV2CdrsMysqlKillEngine(t *testing.T) {
if !*testLocal {
return
}
if err := engine.KillEngine(*waitRater); err != nil {
t.Error(err)
}
}

View File

@@ -0,0 +1,248 @@
/*
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 v2
import (
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
"net/rpc"
"net/rpc/jsonrpc"
"os/exec"
"path"
"testing"
"time"
)
var cdrsPsqlCfgPath string
var cdrsPsqlCfg *config.CGRConfig
var cdrsPsqlRpc *rpc.Client
var cmdEngineCdrPsql *exec.Cmd
func TestV2CdrsPsqlInitConfig(t *testing.T) {
if !*testLocal {
return
}
var err error
cdrsPsqlCfgPath = path.Join(*dataDir, "conf", "samples", "cdrsv2psql")
if cdrsPsqlCfg, err = config.NewCGRConfigFromFolder(cdrsPsqlCfgPath); err != nil {
t.Fatal(err)
}
}
func TestV2CdrsPsqlInitDataDb(t *testing.T) {
if !*testLocal {
return
}
if err := engine.InitDataDb(cdrsPsqlCfg); err != nil {
t.Fatal(err)
}
}
// InitDb so we can rely on count
func TestV2CdrsPsqlInitCdrDb(t *testing.T) {
if !*testLocal {
return
}
if err := engine.InitStorDb(cdrsPsqlCfg); err != nil {
t.Fatal(err)
}
}
func TestV2CdrsPsqlInjectUnratedCdr(t *testing.T) {
if !*testLocal {
return
}
var psqlDb *engine.PostgresStorage
if d, err := engine.NewPostgresStorage(cdrsPsqlCfg.StorDBHost, cdrsPsqlCfg.StorDBPort, cdrsPsqlCfg.StorDBName, cdrsPsqlCfg.StorDBUser, cdrsPsqlCfg.StorDBPass,
cdrsPsqlCfg.StorDBMaxOpenConns, cdrsPsqlCfg.StorDBMaxIdleConns); err != nil {
t.Error("Error on opening database connection: ", err)
return
} else {
psqlDb = d.(*engine.PostgresStorage)
}
strCdr1 := &engine.StoredCdr{CgrId: utils.Sha1("bbb1", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()),
TOR: utils.VOICE, AccId: "bbb1", CdrHost: "192.168.1.1", CdrSource: "UNKNOWN", ReqType: utils.META_RATED,
Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC),
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
MediationRunId: utils.DEFAULT_RUNID, Cost: 1.201}
if err := psqlDb.SetCdr(strCdr1); err != nil {
t.Error(err.Error())
}
}
func TestV2CdrsPsqlStartEngine(t *testing.T) {
if !*testLocal {
return
}
var err error
if cmdEngineCdrPsql, err = engine.StartEngine(cdrsPsqlCfgPath, *waitRater); err != nil {
t.Fatal(err)
}
}
// Connect rpc client to rater
func TestV2CdrsPsqlPsqlRpcConn(t *testing.T) {
if !*testLocal {
return
}
var err error
cdrsPsqlRpc, err = jsonrpc.Dial("tcp", cdrsPsqlCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
if err != nil {
t.Fatal("Could not connect to rater: ", err.Error())
}
}
// Insert some CDRs
func TestV2CdrsPsqlProcessCdr(t *testing.T) {
if !*testLocal {
return
}
var reply string
cdrs := []*engine.StoredCdr{
&engine.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
RatedAccount: "dan", RatedSubject: "dans", Rated: true,
},
&engine.StoredCdr{CgrId: utils.Sha1("abcdeftg", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1002", Subject: "1002", Destination: "1002",
SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
RatedAccount: "dan", RatedSubject: "dans",
},
&engine.StoredCdr{CgrId: utils.Sha1("aererfddf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1003", Subject: "1003", Destination: "1002",
SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
RatedAccount: "dan", RatedSubject: "dans",
},
}
for _, cdr := range cdrs {
if err := cdrsPsqlRpc.Call("CdrsV2.ProcessCdr", cdr, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if reply != utils.OK {
t.Error("Unexpected reply received: ", reply)
}
}
}
func TestV2CdrsPsqlGetCdrs(t *testing.T) {
if !*testLocal {
return
}
var reply []*engine.ExternalCdr
req := utils.RpcCdrsFilter{}
if err := cdrsPsqlRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 4 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
}
// CDRs with errors
req = utils.RpcCdrsFilter{MinCost: utils.Float64Pointer(-1.0), MaxCost: utils.Float64Pointer(0.0)}
if err := cdrsPsqlRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 2 {
t.Error("Unexpected number of CDRs returned: ", reply)
}
// CDRs Rated
req = utils.RpcCdrsFilter{MinCost: utils.Float64Pointer(-1.0)}
if err := cdrsPsqlRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 3 {
t.Error("Unexpected number of CDRs returned: ", reply)
}
// CDRs non rated OR SkipRated
req = utils.RpcCdrsFilter{MaxCost: utils.Float64Pointer(-1.0)}
if err := cdrsPsqlRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 1 {
t.Error("Unexpected number of CDRs returned: ", reply)
}
// Skip Errors
req = utils.RpcCdrsFilter{MinCost: utils.Float64Pointer(0.0), MaxCost: utils.Float64Pointer(-1.0)}
if err := cdrsPsqlRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 2 {
t.Error("Unexpected number of CDRs returned: ", reply)
}
}
func TestV2CdrsPsqlCountCdrs(t *testing.T) {
if !*testLocal {
return
}
var reply int64
req := utils.AttrGetCdrs{}
if err := cdrsPsqlRpc.Call("ApierV2.CountCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if reply != 4 {
t.Error("Unexpected number of CDRs returned: ", reply)
}
}
// Test Prepaid CDRs without previous costs being calculated
func TestV2CdrsPsqlProcessPrepaidCdr(t *testing.T) {
if !*testLocal {
return
}
var reply string
cdrs := []*engine.StoredCdr{
&engine.StoredCdr{CgrId: utils.Sha1("dsafdsaf2", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: utils.META_PREPAID, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
RatedAccount: "dan", RatedSubject: "dans", Rated: true,
},
&engine.StoredCdr{CgrId: utils.Sha1("abcdeftg2", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: utils.META_PREPAID, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1002", Subject: "1002", Destination: "1002",
SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
RatedAccount: "dan", RatedSubject: "dans",
},
&engine.StoredCdr{CgrId: utils.Sha1("aererfddf2", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: utils.META_PREPAID, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1003", Subject: "1003", Destination: "1002",
SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), MediationRunId: utils.DEFAULT_RUNID,
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
RatedAccount: "dan", RatedSubject: "dans",
},
}
tStart := time.Now()
for _, cdr := range cdrs {
if err := cdrsPsqlRpc.Call("CdrsV2.ProcessCdr", cdr, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if reply != utils.OK {
t.Error("Unexpected reply received: ", reply)
}
}
if processDur := time.Now().Sub(tStart); processDur > 1*time.Second {
t.Error("Unexpected processing time", processDur)
}
}
func TestV2CdrsPsqlKillEngine(t *testing.T) {
if !*testLocal {
return
}
if err := engine.KillEngine(*waitRater); err != nil {
t.Error(err)
}
}

99
apier/v2/tp.go Normal file
View File

@@ -0,0 +1,99 @@
/*
Real-time Charging System 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 v2
import (
"encoding/base64"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
type AttrRemTp struct {
TPid string
}
func (self *ApierV2) RemTP(attrs AttrRemTp, reply *string) error {
if len(attrs.TPid) == 0 {
return utils.NewErrMandatoryIeMissing("TPid")
}
if err := self.StorDb.RemTpData("", attrs.TPid); err != nil {
return utils.NewErrServerError(err)
} else {
*reply = "OK"
}
return nil
}
func (self *ApierV2) ExportTPToFolder(attrs utils.AttrDirExportTP, exported *utils.ExportedTPStats) error {
if len(*attrs.TPid) == 0 {
return utils.NewErrMandatoryIeMissing("TPid")
}
dir := self.Config.TpExportPath
if attrs.ExportPath != nil {
dir = *attrs.ExportPath
}
fileFormat := utils.CSV
if attrs.FileFormat != nil {
fileFormat = *attrs.FileFormat
}
sep := ","
if attrs.FieldSeparator != nil {
sep = *attrs.FieldSeparator
}
compress := false
if attrs.Compress != nil {
compress = *attrs.Compress
}
tpExporter, err := engine.NewTPExporter(self.StorDb, *attrs.TPid, dir, fileFormat, sep, compress)
if err != nil {
return utils.NewErrServerError(err)
}
if err := tpExporter.Run(); err != nil {
return utils.NewErrServerError(err)
} else {
*exported = *tpExporter.ExportStats()
}
return nil
}
func (self *ApierV2) ExportTPToZipString(attrs utils.AttrDirExportTP, reply *string) error {
if len(*attrs.TPid) == 0 {
return utils.NewErrMandatoryIeMissing("TPid")
}
dir := ""
fileFormat := utils.CSV
if attrs.FileFormat != nil {
fileFormat = *attrs.FileFormat
}
sep := ","
if attrs.FieldSeparator != nil {
sep = *attrs.FieldSeparator
}
tpExporter, err := engine.NewTPExporter(self.StorDb, *attrs.TPid, dir, fileFormat, sep, true)
if err != nil {
return utils.NewErrServerError(err)
}
if err := tpExporter.Run(); err != nil {
return utils.NewErrServerError(err)
}
*reply = base64.StdEncoding.EncodeToString(tpExporter.GetCacheBuffer().Bytes())
return nil
}

View File

@@ -1,12 +1,14 @@
#! /usr/bin/env sh
go install github.com/cgrates/cgrates/cmd/cgr-rater
echo "Building CGRateS..."
go install github.com/cgrates/cgrates/cmd/cgr-engine
cr=$?
go install github.com/cgrates/cgrates/cmd/cgr-loader
cl=$?
go install github.com/cgrates/cgrates/cmd/cgr-console
cc=$?
go install github.com/cgrates/cgrates/cmd/cgr-tester
ct=$?
exit $cr || $cl || $cc
exit $cr || $cl || $cc || $ct

View File

@@ -2,116 +2,176 @@
package cache2go
import (
"errors"
"sync"
"time"
)
type expiringCacheEntry interface {
XCache(key string, expire time.Duration, value expiringCacheEntry)
timer() *time.Timer
KeepAlive()
const (
PREFIX_LEN = 4
KIND_ADD = "ADD"
KIND_ADP = "ADP"
KIND_REM = "REM"
KIND_PRF = "PRF"
DOUBLE_CACHE = true
)
type timestampedValue struct {
timestamp time.Time
value interface{}
}
// Structure that must be embeded in the objectst that must be cached with expiration.
// If the expiration is not needed this can be ignored
type XEntry struct {
sync.Mutex
key string
keepAlive bool
expireDuration time.Duration
t *time.Timer
func (tsv timestampedValue) Value() interface{} {
return tsv.value
}
type transactionItem struct {
key string
value interface{}
kind string
}
func init() {
if DOUBLE_CACHE {
cache = newDoubleStore()
} else {
cache = newSimpleStore()
}
}
var (
xcache = make(map[string]expiringCacheEntry)
xMux sync.RWMutex
cache = make(map[string]interface{})
mux sync.RWMutex
mux sync.RWMutex
cache cacheStore
// transaction stuff
transactionBuffer []transactionItem
transactionMux sync.Mutex
transactionON = false
transactionLock = false
)
// The main function to cache with expiration
func (xe *XEntry) XCache(key string, expire time.Duration, value expiringCacheEntry) {
xe.keepAlive = true
xe.key = key
xe.expireDuration = expire
xMux.Lock()
xcache[key] = value
xMux.Unlock()
go xe.expire()
func BeginTransaction() {
transactionMux.Lock()
transactionLock = true
transactionON = true
}
// The internal mechanism for expiartion
func (xe *XEntry) expire() {
for xe.keepAlive {
xe.Lock()
xe.keepAlive = false
xe.Unlock()
xe.t = time.NewTimer(xe.expireDuration)
<-xe.t.C
if !xe.keepAlive {
xMux.Lock()
delete(xcache, xe.key)
xMux.Unlock()
func RollbackTransaction() {
transactionBuffer = nil
transactionLock = false
transactionON = false
transactionMux.Unlock()
}
func CommitTransaction() {
transactionON = false
// apply all transactioned items
mux.Lock()
for _, item := range transactionBuffer {
switch item.kind {
case KIND_REM:
RemKey(item.key)
case KIND_PRF:
RemPrefixKey(item.key)
case KIND_ADD:
Cache(item.key, item.value)
case KIND_ADP:
CachePush(item.key, item.value)
}
}
}
// Getter for the timer
func (xe *XEntry) timer() *time.Timer {
return xe.t
}
// Mark entry to be kept another expirationDuration period
func (xe *XEntry) KeepAlive() {
xe.Lock()
defer xe.Unlock()
xe.keepAlive = true
}
// Get an entry from the expiration cache and mark it for keeping alive
func GetXCached(key string) (ece expiringCacheEntry, err error) {
xMux.RLock()
defer xMux.RUnlock()
if r, ok := xcache[key]; ok {
r.KeepAlive()
return r, nil
}
return nil, errors.New("not found")
mux.Unlock()
transactionBuffer = nil
transactionLock = false
transactionMux.Unlock()
}
// The function to be used to cache a key/value pair when expiration is not needed
func Cache(key string, value interface{}) {
mux.Lock()
defer mux.Unlock()
cache[key] = value
if !transactionLock {
mux.Lock()
defer mux.Unlock()
}
if !transactionON {
cache.Put(key, value)
//fmt.Println("ADD: ", key)
} else {
transactionBuffer = append(transactionBuffer, transactionItem{key: key, value: value, kind: KIND_ADD})
}
}
// Appends to an existing slice in the cache key
func CachePush(key string, value interface{}) {
if !transactionLock {
mux.Lock()
defer mux.Unlock()
}
if !transactionON {
cache.Append(key, value)
} else {
transactionBuffer = append(transactionBuffer, transactionItem{key: key, value: value, kind: KIND_ADP})
}
}
// The function to extract a value for a key that never expire
func GetCached(key string) (v interface{}, err error) {
func Get(key string) (v interface{}, err error) {
mux.RLock()
defer mux.RUnlock()
if r, ok := cache[key]; ok {
return r, nil
}
return nil, errors.New("not found")
return cache.Get(key)
}
// Delete all keys from expiraton cache
func XFlush() {
xMux.Lock()
defer xMux.Unlock()
for _, v := range xcache {
if v.timer() != nil {
v.timer().Stop()
}
func GetKeyAge(key string) (time.Duration, error) {
mux.RLock()
defer mux.RUnlock()
return cache.GetAge(key)
}
func RemKey(key string) {
if !transactionLock {
mux.Lock()
defer mux.Unlock()
}
if !transactionON {
cache.Delete(key)
} else {
transactionBuffer = append(transactionBuffer, transactionItem{key: key, kind: KIND_REM})
}
}
func RemPrefixKey(prefix string) {
if !transactionLock {
mux.Lock()
defer mux.Unlock()
}
if !transactionON {
cache.DeletePrefix(prefix)
} else {
transactionBuffer = append(transactionBuffer, transactionItem{key: prefix, kind: KIND_PRF})
}
xcache = make(map[string]expiringCacheEntry)
}
// Delete all keys from cache
func Flush() {
mux.Lock()
defer mux.Unlock()
cache = make(map[string]interface{})
if DOUBLE_CACHE {
cache = newDoubleStore()
} else {
cache = newSimpleStore()
}
}
func CountEntries(prefix string) (result int) {
mux.RLock()
defer mux.RUnlock()
return cache.CountEntriesForPrefix(prefix)
}
func GetAllEntries(prefix string) (map[string]timestampedValue, error) {
mux.RLock()
defer mux.RUnlock()
return cache.GetAllForPrefix(prefix)
}
func GetEntriesKeys(prefix string) (keys []string) {
mux.RLock()
defer mux.RUnlock()
return cache.GetKeysForPrefix(prefix)
}

View File

@@ -1,75 +1,106 @@
package cache2go
import (
"testing"
"time"
)
import "testing"
type myStruct struct {
XEntry
data string
}
func TestCache(t *testing.T) {
a := &myStruct{data: "mama are mere"}
a.XCache("mama", 1*time.Second, a)
b, err := GetXCached("mama")
if err != nil || b == nil || b != a {
t.Error("Error retriving data from cache", err)
func TestRemKey(t *testing.T) {
Cache("t11_mm", "test")
if t1, err := Get("t11_mm"); err != nil || t1 != "test" {
t.Error("Error setting cache: ", err, t1)
}
RemKey("t11_mm")
if t1, err := Get("t11_mm"); err == nil || t1 == "test" {
t.Error("Error removing cached key")
}
}
func TestCacheExpire(t *testing.T) {
a := &myStruct{data: "mama are mere"}
a.XCache("mama", 1*time.Second, a)
b, err := GetXCached("mama")
if err != nil || b == nil || b.(*myStruct).data != "mama are mere" {
t.Error("Error retriving data from cache", err)
func TestTransaction(t *testing.T) {
BeginTransaction()
Cache("t11_mm", "test")
if t1, err := Get("t11_mm"); err == nil || t1 == "test" {
t.Error("Error in transaction cache")
}
time.Sleep(1001 * time.Millisecond)
b, err = GetXCached("mama")
if err == nil || b != nil {
t.Error("Error expiring data")
Cache("t12_mm", "test")
RemKey("t11_mm")
CommitTransaction()
if t1, err := Get("t12_mm"); err != nil || t1 != "test" {
t.Error("Error commiting transaction")
}
if t1, err := Get("t11_mm"); err == nil || t1 == "test" {
t.Error("Error in transaction cache")
}
}
func TestCacheKeepAlive(t *testing.T) {
a := &myStruct{data: "mama are mere"}
a.XCache("mama", 1*time.Second, a)
b, err := GetXCached("mama")
if err != nil || b == nil || b.(*myStruct).data != "mama are mere" {
t.Error("Error retriving data from cache", err)
func TestTransactionRem(t *testing.T) {
BeginTransaction()
Cache("t21_mm", "test")
Cache("t21_nn", "test")
RemPrefixKey("t21_")
CommitTransaction()
if t1, err := Get("t21_mm"); err == nil || t1 == "test" {
t.Error("Error commiting transaction")
}
time.Sleep(500 * time.Millisecond)
b.KeepAlive()
time.Sleep(501 * time.Millisecond)
if err != nil {
t.Error("Error keeping cached data alive", err)
}
time.Sleep(1000 * time.Millisecond)
b, err = GetXCached("mama")
if err == nil || b != nil {
t.Error("Error expiring data")
if t1, err := Get("t21_nn"); err == nil || t1 == "test" {
t.Error("Error in transaction cache")
}
}
func TestFlush(t *testing.T) {
a := &myStruct{data: "mama are mere"}
a.XCache("mama", 10*time.Second, a)
time.Sleep(1000 * time.Millisecond)
XFlush()
b, err := GetXCached("mama")
if err == nil || b != nil {
t.Error("Error expiring data")
func TestTransactionRollback(t *testing.T) {
BeginTransaction()
Cache("t31_mm", "test")
if t1, err := Get("t31_mm"); err == nil || t1 == "test" {
t.Error("Error in transaction cache")
}
Cache("t32_mm", "test")
RollbackTransaction()
if t1, err := Get("t32_mm"); err == nil || t1 == "test" {
t.Error("Error commiting transaction")
}
if t1, err := Get("t31_mm"); err == nil || t1 == "test" {
t.Error("Error in transaction cache")
}
}
func TestFlushNoTimout(t *testing.T) {
a := &myStruct{data: "mama are mere"}
a.XCache("mama", 10*time.Second, a)
XFlush()
b, err := GetXCached("mama")
if err == nil || b != nil {
t.Error("Error expiring data")
func TestTransactionRemBefore(t *testing.T) {
BeginTransaction()
RemPrefixKey("t41_")
Cache("t41_mm", "test")
Cache("t41_nn", "test")
CommitTransaction()
if t1, err := Get("t41_mm"); err != nil || t1 != "test" {
t.Error("Error commiting transaction")
}
if t1, err := Get("t41_nn"); err != nil || t1 != "test" {
t.Error("Error in transaction cache")
}
}
func TestRemPrefixKey(t *testing.T) {
Cache("xxx_t1", "test")
Cache("yyy_t1", "test")
RemPrefixKey("xxx_")
_, errX := Get("xxx_t1")
_, errY := Get("yyy_t1")
if errX == nil || errY != nil {
t.Error("Error removing prefix: ", errX, errY)
}
}
func TestCachePush(t *testing.T) {
CachePush("ccc_t1", "1")
CachePush("ccc_t1", "2")
v, err := Get("ccc_t1")
if err != nil || len(v.(map[interface{}]struct{})) != 2 {
t.Error("Error in cache push: ", v)
}
}
func TestCount(t *testing.T) {
Cache("dst_A1", "1")
Cache("dst_A2", "2")
Cache("rpf_A3", "3")
Cache("dst_A4", "4")
Cache("dst_A5", "5")
if CountEntries("dst_") != 4 {
t.Error("Error countiong entries: ", CountEntries("dst_"))
}
}

View File

@@ -0,0 +1,78 @@
package cache2go
import (
"errors"
"sync"
"time"
"github.com/cgrates/cgrates/utils"
)
var ErrNotFound = errors.New("NOT_FOUND")
type CacheItem struct {
Value interface{}
Err error
}
type ResponseCache struct {
ttl time.Duration
cache map[string]*CacheItem
semaphore map[string]chan bool
mu sync.RWMutex
}
func NewResponseCache(ttl time.Duration) *ResponseCache {
return &ResponseCache{
ttl: ttl,
cache: make(map[string]*CacheItem),
semaphore: make(map[string]chan bool),
mu: sync.RWMutex{},
}
}
func (rc *ResponseCache) Cache(key string, item *CacheItem) {
if rc.ttl == 0 {
return
}
rc.mu.Lock()
rc.cache[key] = item
if _, found := rc.semaphore[key]; found {
close(rc.semaphore[key]) // send release signal
delete(rc.semaphore, key) // delete key
}
rc.mu.Unlock()
go func() {
time.Sleep(rc.ttl)
rc.mu.Lock()
delete(rc.cache, key)
rc.mu.Unlock()
}()
}
func (rc *ResponseCache) Get(key string) (*CacheItem, error) {
if rc.ttl == 0 {
return nil, utils.ErrNotImplemented
}
rc.wait(key) // wait for other goroutine processsing this key
rc.mu.RLock()
defer rc.mu.RUnlock()
item, ok := rc.cache[key]
if !ok {
return nil, ErrNotFound
}
return item, nil
}
func (rc *ResponseCache) wait(key string) {
rc.mu.RLock()
lockChan, found := rc.semaphore[key]
rc.mu.RUnlock()
if found {
<-lockChan
} else {
rc.mu.Lock()
rc.semaphore[key] = make(chan bool)
rc.mu.Unlock()
}
}

View File

@@ -0,0 +1,25 @@
package cache2go
import (
"testing"
"time"
)
func TestRCacheSetGet(t *testing.T) {
rc := NewResponseCache(5 * time.Second)
rc.Cache("test", &CacheItem{Value: "best"})
v, err := rc.Get("test")
if err != nil || v.Value.(string) != "best" {
t.Error("Error retriving response cache: ", v, err)
}
}
func TestRCacheExpire(t *testing.T) {
rc := NewResponseCache(1 * time.Microsecond)
rc.Cache("test", &CacheItem{Value: "best"})
time.Sleep(1 * time.Millisecond)
_, err := rc.Get("test")
if err == nil {
t.Error("Error expiring response cache: ", err)
}
}

223
cache2go/store.go Normal file
View File

@@ -0,0 +1,223 @@
//Simple caching library with expiration capabilities
package cache2go
import (
"strings"
"time"
"github.com/cgrates/cgrates/utils"
)
type cacheStore interface {
Put(string, interface{})
Append(string, interface{})
Get(string) (interface{}, error)
GetAge(string) (time.Duration, error)
Delete(string)
DeletePrefix(string)
CountEntriesForPrefix(string) int
GetAllForPrefix(string) (map[string]timestampedValue, error)
GetKeysForPrefix(string) []string
}
// easy to be counted exported by prefix
type cacheDoubleStore map[string]map[string]timestampedValue
func newDoubleStore() cacheDoubleStore {
return make(cacheDoubleStore)
}
func (cs cacheDoubleStore) Put(key string, value interface{}) {
prefix, key := key[:PREFIX_LEN], key[PREFIX_LEN:]
if _, ok := cs[prefix]; !ok {
cs[prefix] = make(map[string]timestampedValue)
}
cs[prefix][key] = timestampedValue{time.Now(), value}
}
func (cs cacheDoubleStore) Append(key string, value interface{}) {
var elements map[interface{}]struct{} // using map for faster check if element is present
if v, err := cs.Get(key); err == nil {
elements = v.(map[interface{}]struct{})
} else {
elements = make(map[interface{}]struct{})
}
elements[value] = struct{}{}
cache.Put(key, elements)
}
func (cs cacheDoubleStore) Get(key string) (interface{}, error) {
prefix, key := key[:PREFIX_LEN], key[PREFIX_LEN:]
if keyMap, ok := cs[prefix]; ok {
if ti, exists := keyMap[key]; exists {
return ti.value, nil
}
}
return nil, utils.ErrNotFound
}
func (cs cacheDoubleStore) GetAge(key string) (time.Duration, error) {
prefix, key := key[:PREFIX_LEN], key[PREFIX_LEN:]
if keyMap, ok := cs[prefix]; ok {
if ti, exists := keyMap[key]; exists {
return time.Since(ti.timestamp), nil
}
}
return -1, utils.ErrNotFound
}
func (cs cacheDoubleStore) Delete(key string) {
prefix, key := key[:PREFIX_LEN], key[PREFIX_LEN:]
if keyMap, ok := cs[prefix]; ok {
delete(keyMap, key)
}
}
func (cs cacheDoubleStore) DeletePrefix(prefix string) {
delete(cs, prefix)
}
func (cs cacheDoubleStore) CountEntriesForPrefix(prefix string) int {
if m, ok := cs[prefix]; ok {
return len(m)
}
return 0
}
func (cs cacheDoubleStore) GetAllForPrefix(prefix string) (map[string]timestampedValue, error) {
if keyMap, ok := cs[prefix]; ok {
return keyMap, nil
}
return nil, utils.ErrNotFound
}
func (cs cacheDoubleStore) GetKeysForPrefix(prefix string) (keys []string) {
prefix, key := prefix[:PREFIX_LEN], prefix[PREFIX_LEN:]
if keyMap, ok := cs[prefix]; ok {
for iterKey := range keyMap {
if len(key) == 0 || strings.HasPrefix(iterKey, key) {
keys = append(keys, prefix+iterKey)
}
}
}
return
}
// faster to access
type cacheSimpleStore struct {
cache map[string]timestampedValue
counters map[string]int
}
func newSimpleStore() cacheSimpleStore {
return cacheSimpleStore{
cache: make(map[string]timestampedValue),
counters: make(map[string]int),
}
}
func (cs cacheSimpleStore) Put(key string, value interface{}) {
if _, ok := cs.cache[key]; !ok {
// only count if the key is not already there
cs.count(key)
}
cs.cache[key] = timestampedValue{time.Now(), value}
}
func (cs cacheSimpleStore) Append(key string, value interface{}) {
var elements map[interface{}]struct{}
if v, err := cs.Get(key); err == nil {
elements = v.(map[interface{}]struct{})
} else {
elements = make(map[interface{}]struct{})
}
elements[value] = struct{}{}
cache.Put(key, elements)
}
func (cs cacheSimpleStore) Get(key string) (interface{}, error) {
if ti, exists := cs.cache[key]; exists {
return ti.value, nil
}
return nil, utils.ErrNotFound
}
func (cs cacheSimpleStore) GetAge(key string) (time.Duration, error) {
if ti, exists := cs.cache[key]; exists {
return time.Since(ti.timestamp), nil
}
return -1, utils.ErrNotFound
}
func (cs cacheSimpleStore) Delete(key string) {
if _, ok := cs.cache[key]; ok {
delete(cs.cache, key)
cs.descount(key)
}
}
func (cs cacheSimpleStore) DeletePrefix(prefix string) {
for key, _ := range cs.cache {
if strings.HasPrefix(key, prefix) {
delete(cs.cache, key)
cs.descount(key)
}
}
}
// increments the counter for the specified key prefix
func (cs cacheSimpleStore) count(key string) {
if len(key) < PREFIX_LEN {
return
}
prefix := key[:PREFIX_LEN]
if _, ok := cs.counters[prefix]; ok {
// increase the value
cs.counters[prefix] += 1
} else {
cs.counters[prefix] = 1
}
}
// decrements the counter for the specified key prefix
func (cs cacheSimpleStore) descount(key string) {
if len(key) < PREFIX_LEN {
return
}
prefix := key[:PREFIX_LEN]
if value, ok := cs.counters[prefix]; ok && value > 0 {
cs.counters[prefix] -= 1
}
}
func (cs cacheSimpleStore) CountEntriesForPrefix(prefix string) int {
if _, ok := cs.counters[prefix]; ok {
return cs.counters[prefix]
}
return 0
}
func (cs cacheSimpleStore) GetAllForPrefix(prefix string) (map[string]timestampedValue, error) {
result := make(map[string]timestampedValue)
found := false
for key, ti := range cs.cache {
if strings.HasPrefix(key, prefix) {
result[key[PREFIX_LEN:]] = ti
found = true
}
}
if !found {
return nil, utils.ErrNotFound
}
return result, nil
}
func (cs cacheSimpleStore) GetKeysForPrefix(prefix string) (keys []string) {
for key, _ := range cs.cache {
if strings.HasPrefix(key, prefix) {
keys = append(keys, key)
}
}
return
}

9
calls_test.sh Executable file
View File

@@ -0,0 +1,9 @@
#! /usr/bin/env sh
./local_test.sh
lcl=$?
echo 'go test github.com/cgrates/cgrates/general_tests -calls'
go test github.com/cgrates/cgrates/general_tests -calls
gnr=$?
exit $gen && $gnr

297
cdrc/cdrc.go Normal file
View File

@@ -0,0 +1,297 @@
/*
Real-time Charging System 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 cdrc
import (
"bufio"
"encoding/csv"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
"gopkg.in/fsnotify.v1"
)
const (
CSV = "csv"
FS_CSV = "freeswitch_csv"
UNPAIRED_SUFFIX = ".unpaired"
)
// Populates the
func populateStoredCdrField(cdr *engine.StoredCdr, fieldId, fieldVal, timezone string) error {
var err error
switch fieldId {
case utils.TOR:
cdr.TOR += fieldVal
case utils.ACCID:
cdr.AccId += fieldVal
case utils.REQTYPE:
cdr.ReqType += fieldVal
case utils.DIRECTION:
cdr.Direction += fieldVal
case utils.TENANT:
cdr.Tenant += fieldVal
case utils.CATEGORY:
cdr.Category += fieldVal
case utils.ACCOUNT:
cdr.Account += fieldVal
case utils.SUBJECT:
cdr.Subject += fieldVal
case utils.DESTINATION:
cdr.Destination += fieldVal
case utils.SETUP_TIME:
if cdr.SetupTime, err = utils.ParseTimeDetectLayout(fieldVal, timezone); err != nil {
return fmt.Errorf("Cannot parse answer time field with value: %s, err: %s", fieldVal, err.Error())
}
case utils.PDD:
if cdr.Pdd, err = utils.ParseDurationWithSecs(fieldVal); err != nil {
return fmt.Errorf("Cannot parse answer time field with value: %s, err: %s", fieldVal, err.Error())
}
case utils.ANSWER_TIME:
if cdr.AnswerTime, err = utils.ParseTimeDetectLayout(fieldVal, timezone); err != nil {
return fmt.Errorf("Cannot parse answer time field with value: %s, err: %s", fieldVal, err.Error())
}
case utils.USAGE:
if cdr.Usage, err = utils.ParseDurationWithSecs(fieldVal); err != nil {
return fmt.Errorf("Cannot parse duration field with value: %s, err: %s", fieldVal, err.Error())
}
case utils.SUPPLIER:
cdr.Supplier += fieldVal
case utils.DISCONNECT_CAUSE:
cdr.DisconnectCause += fieldVal
default: // Extra fields will not match predefined so they all show up here
cdr.ExtraFields[fieldId] += fieldVal
}
return nil
}
// Understands and processes a specific format of cdr (eg: .csv or .fwv)
type RecordsProcessor interface {
ProcessNextRecord() ([]*engine.StoredCdr, error) // Process a single record in the CDR file, return a slice of CDRs since based on configuration we can have more templates
}
/*
One instance of CDRC will act on one folder.
Common parameters within configs processed:
* cdrS, cdrFormat, cdrInDir, cdrOutDir, runDelay
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) {
var cdrcCfg *config.CdrcConfig
for _, cdrcCfg = range cdrcCfgs { // Take the first config out, does not matter which one
break
}
cdrc := &Cdrc{cdrFormat: cdrcCfg.CdrFormat, cdrInDir: cdrcCfg.CdrInDir, cdrOutDir: cdrcCfg.CdrOutDir,
runDelay: cdrcCfg.RunDelay, csvSep: cdrcCfg.FieldSeparator,
httpSkipTlsCheck: httpSkipTlsCheck, cdrcCfgs: cdrcCfgs, dfltCdrcCfg: cdrcCfg, timezone: utils.FirstNonEmpty(cdrcCfg.Timezone, dfltTimezone), cdrs: cdrs,
closeChan: closeChan, maxOpenFiles: make(chan struct{}, cdrcCfg.MaxOpenFiles),
}
var processFile struct{}
for i := 0; i < cdrcCfg.MaxOpenFiles; i++ {
cdrc.maxOpenFiles <- processFile // Empty initiate so we do not need to wait later when we pop
}
cdrc.cdrSourceIds = make([]string, len(cdrcCfgs))
cdrc.duMultiplyFactors = make([]float64, len(cdrcCfgs))
cdrc.cdrFilters = make([]utils.RSRFields, len(cdrcCfgs))
cdrc.cdrFields = make([][]*config.CfgCdrField, len(cdrcCfgs))
idx := 0
var err error
for _, cfg := range cdrcCfgs {
if idx == 0 { // Steal the config from just one instance since it should be the same for all
cdrc.failedCallsPrefix = cfg.FailedCallsPrefix
if cdrc.partialRecordsCache, err = NewPartialRecordsCache(cdrcCfg.PartialRecordCache, cdrcCfg.CdrOutDir, cdrcCfg.FieldSeparator); err != nil {
return nil, err
}
}
cdrc.cdrSourceIds[idx] = cfg.CdrSourceId
cdrc.duMultiplyFactors[idx] = cfg.DataUsageMultiplyFactor
cdrc.cdrFilters[idx] = cfg.CdrFilter
cdrc.cdrFields[idx] = cfg.ContentFields
idx += 1
}
// Before processing, make sure in and out folders exist
for _, dir := range []string{cdrc.cdrInDir, cdrc.cdrOutDir} {
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
return nil, fmt.Errorf("Nonexistent folder: %s", dir)
}
}
cdrc.httpClient = new(http.Client)
return cdrc, nil
}
type Cdrc struct {
cdrFormat,
cdrInDir,
cdrOutDir string
failedCallsPrefix string // Configured failedCallsPrefix, used in case of flatstore CDRs
cdrSourceIds []string // Should be in sync with cdrFields on indexes
runDelay time.Duration
csvSep rune
duMultiplyFactors []float64
cdrFilters []utils.RSRFields // Should be in sync with cdrFields on indexes
cdrFields [][]*config.CfgCdrField // Profiles directly connected with cdrFilters
httpSkipTlsCheck bool
cdrcCfgs map[string]*config.CdrcConfig // All cdrc config profiles attached to this CDRC (key will be profile instance name)
dfltCdrcCfg *config.CdrcConfig
timezone string
cdrs engine.Connector
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
partialRecordsCache *PartialRecordsCache // Shared between all files in the folder we process
}
// When called fires up folder monitoring, either automated via inotify or manual by sleeping between processing
func (self *Cdrc) Run() error {
if self.runDelay == time.Duration(0) { // Automated via inotify
return self.trackCDRFiles()
}
// Not automated, process and sleep approach
for {
select {
case <-self.closeChan: // Exit, reinject closeChan for other CDRCs
engine.Logger.Info(fmt.Sprintf("<Cdrc> Shutting down CDRC on path %s.", self.cdrInDir))
return nil
default:
}
self.processCdrDir()
time.Sleep(self.runDelay)
}
}
// Watch the specified folder for file moves and parse the files on events
func (self *Cdrc) trackCDRFiles() (err error) {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return
}
defer watcher.Close()
err = watcher.Add(self.cdrInDir)
if err != nil {
return
}
engine.Logger.Info(fmt.Sprintf("<Cdrc> Monitoring %s for file moves.", self.cdrInDir))
for {
select {
case <-self.closeChan: // Exit, reinject closeChan for other CDRCs
engine.Logger.Info(fmt.Sprintf("<Cdrc> Shutting down CDRC on path %s.", self.cdrInDir))
return nil
case ev := <-watcher.Events:
if ev.Op&fsnotify.Create == fsnotify.Create && (self.cdrFormat != FS_CSV || path.Ext(ev.Name) != ".csv") {
go func() { //Enable async processing here
if err = self.processFile(ev.Name); err != nil {
engine.Logger.Err(fmt.Sprintf("Processing file %s, error: %s", ev.Name, err.Error()))
}
}()
}
case err := <-watcher.Errors:
engine.Logger.Err(fmt.Sprintf("Inotify error: %s", err.Error()))
}
}
}
// One run over the CDR folder
func (self *Cdrc) processCdrDir() error {
engine.Logger.Info(fmt.Sprintf("<Cdrc> Parsing folder %s for CDR files.", self.cdrInDir))
filesInDir, _ := ioutil.ReadDir(self.cdrInDir)
for _, file := range filesInDir {
if self.cdrFormat != FS_CSV || path.Ext(file.Name()) != ".csv" {
go func() { //Enable async processing here
if err := self.processFile(path.Join(self.cdrInDir, file.Name())); err != nil {
engine.Logger.Err(fmt.Sprintf("Processing file %s, error: %s", file, err.Error()))
}
}()
}
}
return nil
}
// Processe file at filePath and posts the valid cdr rows out of it
func (self *Cdrc) processFile(filePath string) error {
if cap(self.maxOpenFiles) != 0 { // 0 goes for no limit
processFile := <-self.maxOpenFiles // Queue here for maxOpenFiles
defer func() { self.maxOpenFiles <- processFile }()
}
_, fn := path.Split(filePath)
engine.Logger.Info(fmt.Sprintf("<Cdrc> Parsing: %s", filePath))
file, err := os.Open(filePath)
defer file.Close()
if err != nil {
engine.Logger.Crit(err.Error())
return err
}
var recordsProcessor RecordsProcessor
switch self.cdrFormat {
case CSV, FS_CSV, utils.KAM_FLATSTORE, utils.OSIPS_FLATSTORE:
csvReader := csv.NewReader(bufio.NewReader(file))
csvReader.Comma = self.csvSep
recordsProcessor = NewCsvRecordsProcessor(csvReader, self.cdrFormat, self.timezone, fn, self.failedCallsPrefix,
self.cdrSourceIds, self.duMultiplyFactors, self.cdrFilters, self.cdrFields, self.httpSkipTlsCheck, self.partialRecordsCache)
case utils.FWV:
recordsProcessor = NewFwvRecordsProcessor(file, self.cdrcCfgs, self.dfltCdrcCfg, self.httpClient, self.httpSkipTlsCheck, self.timezone)
default:
return fmt.Errorf("Unsupported CDR format: %s", self.cdrFormat)
}
procRowNr := 0
cdrsPosted := 0
timeStart := time.Now()
for {
cdrs, err := recordsProcessor.ProcessNextRecord()
if err != nil && err == io.EOF {
break
}
procRowNr += 1
if err != nil {
engine.Logger.Err(fmt.Sprintf("<Cdrc> Row %d, error: %s", procRowNr, err.Error()))
continue
}
for _, storedCdr := range cdrs { // Send CDRs to CDRS
var reply string
if self.dfltCdrcCfg.DryRun {
engine.Logger.Info(fmt.Sprintf("<Cdrc> DryRun CDR: %+v", storedCdr))
continue
}
if err := self.cdrs.ProcessCdr(storedCdr, &reply); err != nil {
engine.Logger.Err(fmt.Sprintf("<Cdrc> Failed sending CDR, %+v, error: %s", storedCdr, err.Error()))
} else if reply != "OK" {
engine.Logger.Err(fmt.Sprintf("<Cdrc> Received unexpected reply for CDR, %+v, reply: %s", storedCdr, reply))
} else {
cdrsPosted += 1
}
}
}
// Finished with file, move it to processed folder
newPath := path.Join(self.cdrOutDir, fn)
if err := os.Rename(filePath, newPath); err != nil {
engine.Logger.Err(err.Error())
return err
}
engine.Logger.Info(fmt.Sprintf("Finished processing %s, moved to %s. Total records processed: %d, CDRs posted: %d, run duration: %s",
fn, newPath, procRowNr, cdrsPosted, time.Now().Sub(timeStart)))
return nil
}

223
cdrc/cdrc_local_test.go Normal file
View File

@@ -0,0 +1,223 @@
/*
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 TestLoadConfigt(*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 TestEmptyTables(t *testing.T) {
if !*testLocal {
return
}
if *storDbType != utils.MYSQL {
t.Fatal("Unsupported storDbType")
}
var mysql *engine.MySQLStorage
if d, err := engine.NewMySQLStorage(cfg.StorDBHost, cfg.StorDBPort, cfg.StorDBName, cfg.StorDBUser, cfg.StorDBPass, cfg.StorDBMaxOpenConns, cfg.StorDBMaxIdleConns); err != nil {
t.Fatal("Error on opening database connection: ", err)
} else {
mysql = d.(*engine.MySQLStorage)
}
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
}
}
for _, tbl := range []string{utils.TBL_CDRS_PRIMARY, utils.TBL_CDRS_EXTRA} {
if _, err := mysql.Db.Query(fmt.Sprintf("SELECT 1 from %s", tbl)); err != nil {
t.Fatal(err.Error())
}
}
}
// Creates cdr files and starts the engine
func TestCreateCdrFiles(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 TestProcessCdrDir(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 TestCreateCdr3File(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 TestProcessCdr3Dir(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()
}

318
cdrc/cdrc_test.go Normal file
View File

@@ -0,0 +1,318 @@
/*
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
/*
func TestNewPartialFlatstoreRecord(t *testing.T) {
ePr := &PartialFlatstoreRecord{Method: "INVITE", AccId: "dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:02daec40c548625ac", Timestamp: time.Date(2015, 7, 9, 15, 6, 48, 0, time.UTC),
Values: []string{"INVITE", "2daec40c", "548625ac", "dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:0", "200", "OK", "1436454408", "*prepaid", "1001", "1002", "", "3401:2069362475"}}
if pr, err := NewPartialFlatstoreRecord(ePr.Values); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(ePr, pr) {
t.Errorf("Expecting: %+v, received: %+v", ePr, pr)
}
if _, err := NewPartialFlatstoreRecord([]string{"INVITE", "2daec40c", "548625ac", "dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:0", "200", "OK"}); err == nil || err.Error() != "MISSING_IE" {
t.Error(err)
}
}
*/
/*
func TestOsipsFlatstoreCdrs(t *testing.T) {
flatstoreCdrs := `
INVITE|2daec40c|548625ac|dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:0|200|OK|1436454408|*prepaid|1001|1002||3401:2069362475
BYE|2daec40c|548625ac|dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:0|200|OK|1436454410|||||3401:2069362475
INVITE|f9d3d5c3|c863a6e3|214d8f52b566e33a9349b184e72a4cca@0:0:0:0:0:0:0:0|200|OK|1436454647|*postpaid|1002|1001||1877:893549741
BYE|f9d3d5c3|c863a6e3|214d8f52b566e33a9349b184e72a4cca@0:0:0:0:0:0:0:0|200|OK|1436454651|||||1877:893549741
INVITE|36e39a5|42d996f9|3a63321dd3b325eec688dc2aefb6ac2d@0:0:0:0:0:0:0:0|200|OK|1436454657|*prepaid|1001|1002||2407:1884881533
BYE|36e39a5|42d996f9|3a63321dd3b325eec688dc2aefb6ac2d@0:0:0:0:0:0:0:0|200|OK|1436454661|||||2407:1884881533
INVITE|3111f3c9|49ca4c42|a58ebaae40d08d6757d8424fb09c4c54@0:0:0:0:0:0:0:0|200|OK|1436454690|*prepaid|1001|1002||3099:1909036290
BYE|3111f3c9|49ca4c42|a58ebaae40d08d6757d8424fb09c4c54@0:0:0:0:0:0:0:0|200|OK|1436454692|||||3099:1909036290
`
eCdrs := []*engine.StoredCdr{
&engine.StoredCdr{
CgrId: "e61034c34148a7c4f40623e00ca5e551d1408bf3",
TOR: utils.VOICE,
AccId: "dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:02daec40c548625ac",
CdrHost: "0.0.0.0",
CdrSource: "TEST_CDRC",
ReqType: utils.META_PREPAID,
Direction: "*out",
Tenant: "cgrates.org",
Category: "call",
Account: "1001",
Subject: "1001",
Destination: "1002",
SetupTime: time.Date(2015, 7, 9, 15, 06, 48, 0, time.UTC),
AnswerTime: time.Date(2015, 7, 9, 15, 06, 48, 0, time.UTC),
Usage: time.Duration(2) * time.Second,
DisconnectCause: "200 OK",
ExtraFields: map[string]string{
"DialogIdentifier": "3401:2069362475",
},
Cost: -1,
},
&engine.StoredCdr{
CgrId: "3ed64a28190e20ac8a6fd8fd48cb23efbfeb7a17",
TOR: utils.VOICE,
AccId: "214d8f52b566e33a9349b184e72a4cca@0:0:0:0:0:0:0:0f9d3d5c3c863a6e3",
CdrHost: "0.0.0.0",
CdrSource: "TEST_CDRC",
ReqType: utils.META_POSTPAID,
Direction: "*out",
Tenant: "cgrates.org",
Category: "call",
Account: "1002",
Subject: "1002",
Destination: "1001",
SetupTime: time.Date(2015, 7, 9, 15, 10, 47, 0, time.UTC),
AnswerTime: time.Date(2015, 7, 9, 15, 10, 47, 0, time.UTC),
Usage: time.Duration(4) * time.Second,
DisconnectCause: "200 OK",
ExtraFields: map[string]string{
"DialogIdentifier": "1877:893549741",
},
Cost: -1,
},
&engine.StoredCdr{
CgrId: "f2f8d9341adfbbe1836b22f75182142061ef3d20",
TOR: utils.VOICE,
AccId: "3a63321dd3b325eec688dc2aefb6ac2d@0:0:0:0:0:0:0:036e39a542d996f9",
CdrHost: "0.0.0.0",
CdrSource: "TEST_CDRC",
ReqType: utils.META_PREPAID,
Direction: "*out",
Tenant: "cgrates.org",
Category: "call",
Account: "1001",
Subject: "1001",
Destination: "1002",
SetupTime: time.Date(2015, 7, 9, 15, 10, 57, 0, time.UTC),
AnswerTime: time.Date(2015, 7, 9, 15, 10, 57, 0, time.UTC),
Usage: time.Duration(4) * time.Second,
DisconnectCause: "200 OK",
ExtraFields: map[string]string{
"DialogIdentifier": "2407:1884881533",
},
Cost: -1,
},
&engine.StoredCdr{
CgrId: "ccf05e7e3b9db9d2370bcbe316817447dba7df54",
TOR: utils.VOICE,
AccId: "a58ebaae40d08d6757d8424fb09c4c54@0:0:0:0:0:0:0:03111f3c949ca4c42",
CdrHost: "0.0.0.0",
CdrSource: "TEST_CDRC",
ReqType: utils.META_PREPAID,
Direction: "*out",
Tenant: "cgrates.org",
Category: "call",
Account: "1001",
Subject: "1001",
Destination: "1002",
SetupTime: time.Date(2015, 7, 9, 15, 11, 30, 0, time.UTC), //2015-07-09T17:11:30+02:00
AnswerTime: time.Date(2015, 7, 9, 15, 11, 30, 0, time.UTC),
Usage: time.Duration(2) * time.Second,
DisconnectCause: "200 OK",
ExtraFields: map[string]string{
"DialogIdentifier": "3099:1909036290",
},
Cost: -1,
},
}
cdrFields := [][]*config.CfgCdrField{[]*config.CfgCdrField{
&config.CfgCdrField{Tag: "Tor", Type: utils.CDRFIELD, CdrFieldId: utils.TOR, Value: utils.ParseRSRFieldsMustCompile("^*voice", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "AccId", Type: utils.CDRFIELD, CdrFieldId: utils.ACCID, Mandatory: true},
&config.CfgCdrField{Tag: "ReqType", Type: utils.CDRFIELD, CdrFieldId: utils.REQTYPE, Value: utils.ParseRSRFieldsMustCompile("7", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "Direction", Type: utils.CDRFIELD, CdrFieldId: utils.DIRECTION, Value: utils.ParseRSRFieldsMustCompile("^*out", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "Direction", Type: utils.CDRFIELD, CdrFieldId: utils.DIRECTION, Value: utils.ParseRSRFieldsMustCompile("^*out", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "Tenant", Type: utils.CDRFIELD, CdrFieldId: utils.TENANT, Value: utils.ParseRSRFieldsMustCompile("^cgrates.org", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "Category", Type: utils.CDRFIELD, CdrFieldId: utils.CATEGORY, Value: utils.ParseRSRFieldsMustCompile("^call", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "Account", Type: utils.CDRFIELD, CdrFieldId: utils.ACCOUNT, Value: utils.ParseRSRFieldsMustCompile("8", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "Subject", Type: utils.CDRFIELD, CdrFieldId: utils.SUBJECT, Value: utils.ParseRSRFieldsMustCompile("8", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "Destination", Type: utils.CDRFIELD, CdrFieldId: utils.DESTINATION, Value: utils.ParseRSRFieldsMustCompile("9", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "SetupTime", Type: utils.CDRFIELD, CdrFieldId: utils.SETUP_TIME, Value: utils.ParseRSRFieldsMustCompile("6", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "AnswerTime", Type: utils.CDRFIELD, CdrFieldId: utils.ANSWER_TIME, Value: utils.ParseRSRFieldsMustCompile("6", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "Duration", Type: utils.CDRFIELD, CdrFieldId: utils.USAGE, Mandatory: true},
&config.CfgCdrField{Tag: "DisconnectCause", Type: utils.CDRFIELD, CdrFieldId: utils.DISCONNECT_CAUSE, Value: utils.ParseRSRFieldsMustCompile("4;^ ;5", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "DialogId", Type: utils.CDRFIELD, CdrFieldId: "DialogIdentifier", Value: utils.ParseRSRFieldsMustCompile("11", utils.INFIELD_SEP)},
}}
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()}
cdrsContent := bytes.NewReader([]byte(flatstoreCdrs))
csvReader := csv.NewReader(cdrsContent)
csvReader.Comma = '|'
cdrs := make([]*engine.StoredCdr, 0)
recNrs := 0
for {
recNrs++
cdrCsv, err := csvReader.Read()
if err != nil && err == io.EOF {
break // End of file
} else if err != nil {
t.Error("Unexpected error:", err)
}
record, err := cdrc.processPartialRecord(cdrCsv, "dummyfilename")
if err != nil {
t.Error(err)
}
if record == nil {
continue // Partial record
}
if storedCdr, err := cdrc.recordToStoredCdr(record, 0); err != nil {
t.Error(err)
} else if storedCdr != nil {
cdrs = append(cdrs, storedCdr)
}
}
if !reflect.DeepEqual(eCdrs, cdrs) {
t.Errorf("Expecting: %+v, received: %+v", eCdrs, cdrs)
}
}
func TestOsipsFlatstoreMissedCdrs(t *testing.T) {
flatstoreCdrs := `
INVITE|ef6c6256|da501581|0bfdd176d1b93e7df3de5c6f4873ee04@0:0:0:0:0:0:0:0|487|Request Terminated|1436454643|*prepaid|1001|1002||1224:339382783
INVITE|7905e511||81880da80a94bda81b425b09009e055c@0:0:0:0:0:0:0:0|404|Not Found|1436454668|*prepaid|1001|1002||1980:1216490844
INVITE|324cb497|d4af7023|8deaadf2ae9a17809a391f05af31afb0@0:0:0:0:0:0:0:0|486|Busy here|1436454687|*postpaid|1002|1001||474:130115066
`
eCdrs := []*engine.StoredCdr{
&engine.StoredCdr{
CgrId: "1c20aa6543a5a30d26b2354ae79e1f5fb720e8e5",
TOR: utils.VOICE,
AccId: "0bfdd176d1b93e7df3de5c6f4873ee04@0:0:0:0:0:0:0:0ef6c6256da501581",
CdrHost: "0.0.0.0",
CdrSource: "TEST_CDRC",
ReqType: utils.META_PREPAID,
Direction: "*out",
Tenant: "cgrates.org",
Category: "call",
Account: "1001",
Subject: "1001",
Destination: "1002",
SetupTime: time.Date(2015, 7, 9, 15, 10, 43, 0, time.UTC),
AnswerTime: time.Date(2015, 7, 9, 15, 10, 43, 0, time.UTC),
Usage: 0,
DisconnectCause: "487 Request Terminated",
ExtraFields: map[string]string{
"DialogIdentifier": "1224:339382783",
},
Cost: -1,
},
&engine.StoredCdr{
CgrId: "054ab7c6c7fe6dc4a72f34e270027fa2aa930a58",
TOR: utils.VOICE,
AccId: "81880da80a94bda81b425b09009e055c@0:0:0:0:0:0:0:07905e511",
CdrHost: "0.0.0.0",
CdrSource: "TEST_CDRC",
ReqType: utils.META_PREPAID,
Direction: "*out",
Tenant: "cgrates.org",
Category: "call",
Account: "1001",
Subject: "1001",
Destination: "1002",
SetupTime: time.Date(2015, 7, 9, 15, 11, 8, 0, time.UTC),
AnswerTime: time.Date(2015, 7, 9, 15, 11, 8, 0, time.UTC),
Usage: 0,
DisconnectCause: "404 Not Found",
ExtraFields: map[string]string{
"DialogIdentifier": "1980:1216490844",
},
Cost: -1,
},
&engine.StoredCdr{
CgrId: "d49ea63d1655b15149336004629f1cadd1434b89",
TOR: utils.VOICE,
AccId: "8deaadf2ae9a17809a391f05af31afb0@0:0:0:0:0:0:0:0324cb497d4af7023",
CdrHost: "0.0.0.0",
CdrSource: "TEST_CDRC",
ReqType: utils.META_POSTPAID,
Direction: "*out",
Tenant: "cgrates.org",
Category: "call",
Account: "1002",
Subject: "1002",
Destination: "1001",
SetupTime: time.Date(2015, 7, 9, 15, 11, 27, 0, time.UTC),
AnswerTime: time.Date(2015, 7, 9, 15, 11, 27, 0, time.UTC),
Usage: 0,
DisconnectCause: "486 Busy here",
ExtraFields: map[string]string{
"DialogIdentifier": "474:130115066",
},
Cost: -1,
},
}
cdrFields := [][]*config.CfgCdrField{[]*config.CfgCdrField{
&config.CfgCdrField{Tag: "Tor", Type: utils.CDRFIELD, CdrFieldId: utils.TOR, Value: utils.ParseRSRFieldsMustCompile("^*voice", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "AccId", Type: utils.CDRFIELD, CdrFieldId: utils.ACCID, Mandatory: true},
&config.CfgCdrField{Tag: "ReqType", Type: utils.CDRFIELD, CdrFieldId: utils.REQTYPE, Value: utils.ParseRSRFieldsMustCompile("7", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "Direction", Type: utils.CDRFIELD, CdrFieldId: utils.DIRECTION, Value: utils.ParseRSRFieldsMustCompile("^*out", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "Direction", Type: utils.CDRFIELD, CdrFieldId: utils.DIRECTION, Value: utils.ParseRSRFieldsMustCompile("^*out", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "Tenant", Type: utils.CDRFIELD, CdrFieldId: utils.TENANT, Value: utils.ParseRSRFieldsMustCompile("^cgrates.org", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "Category", Type: utils.CDRFIELD, CdrFieldId: utils.CATEGORY, Value: utils.ParseRSRFieldsMustCompile("^call", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "Account", Type: utils.CDRFIELD, CdrFieldId: utils.ACCOUNT, Value: utils.ParseRSRFieldsMustCompile("8", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "Subject", Type: utils.CDRFIELD, CdrFieldId: utils.SUBJECT, Value: utils.ParseRSRFieldsMustCompile("8", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "Destination", Type: utils.CDRFIELD, CdrFieldId: utils.DESTINATION, Value: utils.ParseRSRFieldsMustCompile("9", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "SetupTime", Type: utils.CDRFIELD, CdrFieldId: utils.SETUP_TIME, Value: utils.ParseRSRFieldsMustCompile("6", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "AnswerTime", Type: utils.CDRFIELD, CdrFieldId: utils.ANSWER_TIME, Value: utils.ParseRSRFieldsMustCompile("6", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "Usage", Type: utils.CDRFIELD, CdrFieldId: utils.USAGE, Mandatory: true},
&config.CfgCdrField{Tag: "DisconnectCause", Type: utils.CDRFIELD, CdrFieldId: utils.DISCONNECT_CAUSE, Value: utils.ParseRSRFieldsMustCompile("4;^ ;5", utils.INFIELD_SEP), Mandatory: true},
&config.CfgCdrField{Tag: "DialogId", Type: utils.CDRFIELD, CdrFieldId: "DialogIdentifier", Value: utils.ParseRSRFieldsMustCompile("11", utils.INFIELD_SEP)},
}}
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()}
cdrsContent := bytes.NewReader([]byte(flatstoreCdrs))
csvReader := csv.NewReader(cdrsContent)
csvReader.Comma = '|'
cdrs := make([]*engine.StoredCdr, 0)
recNrs := 0
for {
recNrs++
cdrCsv, err := csvReader.Read()
if err != nil && err == io.EOF {
break // End of file
} else if err != nil {
t.Error("Unexpected error:", err)
}
record, err := cdrc.processPartialRecord(cdrCsv, "missed_calls_1.log")
if err != nil {
t.Error(err)
}
if record == nil {
continue // Partial record
}
if storedCdr, err := cdrc.recordToStoredCdr(record, 0); err != nil {
t.Error(err)
} else if storedCdr != nil {
cdrs = append(cdrs, storedCdr)
}
}
if !reflect.DeepEqual(eCdrs, cdrs) {
t.Errorf("Expecting: %+v, received: %+v", eCdrs, cdrs)
}
}
*/

335
cdrc/csv.go Normal file
View File

@@ -0,0 +1,335 @@
/*
Real-time Charging System 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 cdrc
import (
"encoding/csv"
"errors"
"fmt"
"os"
"path"
"strconv"
"strings"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
func NewPartialFlatstoreRecord(record []string, timezone string) (*PartialFlatstoreRecord, error) {
if len(record) < 7 {
return nil, errors.New("MISSING_IE")
}
pr := &PartialFlatstoreRecord{Method: record[0], AccId: record[3] + record[1] + record[2], Values: record}
var err error
if pr.Timestamp, err = utils.ParseTimeDetectLayout(record[6], timezone); err != nil {
return nil, err
}
return pr, nil
}
// This is a partial record received from Flatstore, can be INVITE or BYE and it needs to be paired in order to produce duration
type PartialFlatstoreRecord struct {
Method string // INVITE or BYE
AccId string // Copute here the AccId
Timestamp time.Time // Timestamp of the event, as written by db_flastore module
Values []string // Can contain original values or updated via UpdateValues
}
// Pairs INVITE and BYE into final record containing as last element the duration
func pairToRecord(part1, part2 *PartialFlatstoreRecord) ([]string, error) {
var invite, bye *PartialFlatstoreRecord
if part1.Method == "INVITE" {
invite = part1
} else if part2.Method == "INVITE" {
invite = part2
} else {
return nil, errors.New("MISSING_INVITE")
}
if part1.Method == "BYE" {
bye = part1
} else if part2.Method == "BYE" {
bye = part2
} else {
return nil, errors.New("MISSING_BYE")
}
if len(invite.Values) != len(bye.Values) {
return nil, errors.New("INCONSISTENT_VALUES_LENGTH")
}
record := invite.Values
for idx := range record {
switch idx {
case 0, 1, 2, 3, 6: // Leave these values as they are
case 4, 5:
record[idx] = bye.Values[idx] // Update record with status from bye
default:
if bye.Values[idx] != "" { // Any value higher than 6 is dynamically inserted, overwrite if non empty
record[idx] = bye.Values[idx]
}
}
}
callDur := bye.Timestamp.Sub(invite.Timestamp)
record = append(record, strconv.FormatFloat(callDur.Seconds(), 'f', -1, 64))
return record, nil
}
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
}
type PartialRecordsCache struct {
ttl time.Duration
cdrOutDir string
csvSep rune
partialRecords map[string]map[string]*PartialFlatstoreRecord // [FileName"][AccId]*PartialRecord
guard *engine.GuardianLock
}
// Dumps the cache into a .unpaired file in the outdir and cleans cache after
func (self *PartialRecordsCache) dumpUnpairedRecords(fileName string) error {
_, err := self.guard.Guard(func() (interface{}, error) {
if len(self.partialRecords[fileName]) != 0 { // Only write the file if there are records in the cache
unpairedFilePath := path.Join(self.cdrOutDir, fileName+UNPAIRED_SUFFIX)
fileOut, err := os.Create(unpairedFilePath)
if err != nil {
engine.Logger.Err(fmt.Sprintf("<Cdrc> Failed creating %s, error: %s", unpairedFilePath, err.Error()))
return nil, err
}
csvWriter := csv.NewWriter(fileOut)
csvWriter.Comma = self.csvSep
for _, pr := range self.partialRecords[fileName] {
if err := csvWriter.Write(pr.Values); err != nil {
engine.Logger.Err(fmt.Sprintf("<Cdrc> Failed writing unpaired record %v to file: %s, error: %s", pr, unpairedFilePath, err.Error()))
return nil, err
}
}
csvWriter.Flush()
}
delete(self.partialRecords, fileName)
return nil, nil
}, 0, fileName)
return err
}
// Search in cache and return the partial record with accountind id defined, prefFilename is searched at beginning because of better match probability
func (self *PartialRecordsCache) GetPartialRecord(accId, prefFileName string) (string, *PartialFlatstoreRecord) {
var cachedFilename string
var cachedPartial *PartialFlatstoreRecord
checkCachedFNames := []string{prefFileName} // Higher probability to match as firstFileName
for fName := range self.partialRecords {
if fName != prefFileName {
checkCachedFNames = append(checkCachedFNames, fName)
}
}
for _, fName := range checkCachedFNames { // Need to lock them individually
self.guard.Guard(func() (interface{}, error) {
var hasPartial bool
if cachedPartial, hasPartial = self.partialRecords[fName][accId]; hasPartial {
cachedFilename = fName
}
return nil, nil
}, 0, fName)
if cachedPartial != nil {
break
}
}
return cachedFilename, cachedPartial
}
func (self *PartialRecordsCache) CachePartial(fileName string, pr *PartialFlatstoreRecord) {
self.guard.Guard(func() (interface{}, error) {
if fileMp, hasFile := self.partialRecords[fileName]; !hasFile {
self.partialRecords[fileName] = map[string]*PartialFlatstoreRecord{pr.AccId: pr}
if self.ttl != 0 { // Schedule expiry/dump of the just created entry in cache
go func() {
time.Sleep(self.ttl)
self.dumpUnpairedRecords(fileName)
}()
}
} else if _, hasAccId := fileMp[pr.AccId]; !hasAccId {
self.partialRecords[fileName][pr.AccId] = pr
}
return nil, nil
}, 0, fileName)
}
func (self *PartialRecordsCache) UncachePartial(fileName string, pr *PartialFlatstoreRecord) {
self.guard.Guard(func() (interface{}, error) {
delete(self.partialRecords[fileName], pr.AccId) // Remove the record out of cache
return nil, nil
}, 0, fileName)
}
func NewCsvRecordsProcessor(csvReader *csv.Reader, cdrFormat, timezone, fileName, failedCallsPrefix string,
cdrSourceIds []string, duMultiplyFactors []float64, cdrFilters []utils.RSRFields, cdrFields [][]*config.CfgCdrField,
httpSkipTlsCheck bool, partialRecordsCache *PartialRecordsCache) *CsvRecordsProcessor {
return &CsvRecordsProcessor{csvReader: csvReader, cdrFormat: cdrFormat, timezone: timezone, fileName: fileName,
failedCallsPrefix: failedCallsPrefix, cdrSourceIds: cdrSourceIds,
duMultiplyFactors: duMultiplyFactors, cdrFilters: cdrFilters, cdrFields: cdrFields,
httpSkipTlsCheck: httpSkipTlsCheck, partialRecordsCache: partialRecordsCache}
}
type CsvRecordsProcessor struct {
csvReader *csv.Reader
cdrFormat string
timezone string // Timezone for CDRs which are not clearly specifying it
fileName string
failedCallsPrefix string
cdrSourceIds []string // Should be in sync with cdrFields on indexes
duMultiplyFactors []float64
cdrFilters []utils.RSRFields // Should be in sync with cdrFields on indexes
cdrFields [][]*config.CfgCdrField // Profiles directly connected with cdrFilters
httpSkipTlsCheck bool
partialRecordsCache *PartialRecordsCache // Shared by cdrc so we can cache for all files in a folder
}
func (self *CsvRecordsProcessor) ProcessNextRecord() ([]*engine.StoredCdr, error) {
record, err := self.csvReader.Read()
if err != nil {
return nil, err
}
if utils.IsSliceMember([]string{utils.KAM_FLATSTORE, utils.OSIPS_FLATSTORE}, self.cdrFormat) {
if record, err = self.processPartialRecord(record); err != nil {
return nil, err
} else if record == nil {
return nil, nil // Due to partial, none returned
}
}
// Record was overwriten with complete information out of cache
return self.processRecord(record)
}
// Processes a single partial record for flatstore CDRs
func (self *CsvRecordsProcessor) processPartialRecord(record []string) ([]string, error) {
if strings.HasPrefix(self.fileName, self.failedCallsPrefix) { // Use the first index since they should be the same in all configs
record = append(record, "0") // Append duration 0 for failed calls flatstore CDR and do not process it further
return record, nil
}
pr, err := NewPartialFlatstoreRecord(record, self.timezone)
if err != nil {
return nil, err
}
// Retrieve and complete the record from cache
cachedFilename, cachedPartial := self.partialRecordsCache.GetPartialRecord(pr.AccId, self.fileName)
if cachedPartial == nil { // Not cached, do it here and stop processing
self.partialRecordsCache.CachePartial(self.fileName, pr)
return nil, nil
}
pairedRecord, err := pairToRecord(cachedPartial, pr)
if err != nil {
return nil, err
}
self.partialRecordsCache.UncachePartial(cachedFilename, pr)
return pairedRecord, nil
}
// Takes the record from a slice and turns it into StoredCdrs, posting them to the cdrServer
func (self *CsvRecordsProcessor) processRecord(record []string) ([]*engine.StoredCdr, error) {
recordCdrs := make([]*engine.StoredCdr, 0) // More CDRs based on the number of filters and field templates
for idx := range self.cdrFields { // cdrFields coming from more templates will produce individual storCdr records
// Make sure filters are matching
filterBreak := false
for _, rsrFilter := range self.cdrFilters[idx] {
if rsrFilter == nil { // Nil filter does not need to match anything
continue
}
if cfgFieldIdx, _ := strconv.Atoi(rsrFilter.Id); len(record) <= cfgFieldIdx {
return nil, fmt.Errorf("Ignoring record: %v - cannot compile filter %+v", record, rsrFilter)
} else if !rsrFilter.FilterPasses(record[cfgFieldIdx]) {
filterBreak = true
break
}
}
if filterBreak { // Stop importing cdrc fields profile due to non matching filter
continue
}
if storedCdr, err := self.recordToStoredCdr(record, idx); err != nil {
return nil, fmt.Errorf("Failed converting to StoredCdr, error: %s", err.Error())
} else {
recordCdrs = append(recordCdrs, storedCdr)
}
}
return recordCdrs, nil
}
// Takes the record out of csv and turns it into storedCdr which can be processed by CDRS
func (self *CsvRecordsProcessor) recordToStoredCdr(record []string, cfgIdx int) (*engine.StoredCdr, error) {
storedCdr := &engine.StoredCdr{CdrHost: "0.0.0.0", CdrSource: self.cdrSourceIds[cfgIdx], ExtraFields: make(map[string]string), Cost: -1}
var err error
var lazyHttpFields []*config.CfgCdrField
for _, cdrFldCfg := range self.cdrFields[cfgIdx] {
if utils.IsSliceMember([]string{utils.KAM_FLATSTORE, utils.OSIPS_FLATSTORE}, self.cdrFormat) { // Hardcode some values in case of flatstore
switch cdrFldCfg.CdrFieldId {
case utils.ACCID:
cdrFldCfg.Value = utils.ParseRSRFieldsMustCompile("3;1;2", utils.INFIELD_SEP) // in case of flatstore, accounting id is made up out of callid, from_tag and to_tag
case utils.USAGE:
cdrFldCfg.Value = utils.ParseRSRFieldsMustCompile(strconv.Itoa(len(record)-1), utils.INFIELD_SEP) // in case of flatstore, last element will be the duration computed by us
}
}
var fieldVal string
if cdrFldCfg.Type == utils.CDRFIELD {
for _, cfgFieldRSR := range cdrFldCfg.Value {
if cfgFieldRSR.IsStatic() {
fieldVal += cfgFieldRSR.ParseValue("")
} else { // Dynamic value extracted using index
if cfgFieldIdx, _ := strconv.Atoi(cfgFieldRSR.Id); len(record) <= cfgFieldIdx {
return nil, fmt.Errorf("Ignoring record: %v - cannot extract field %s", record, cdrFldCfg.Tag)
} else {
fieldVal += cfgFieldRSR.ParseValue(record[cfgFieldIdx])
}
}
}
} else if cdrFldCfg.Type == utils.HTTP_POST {
lazyHttpFields = append(lazyHttpFields, cdrFldCfg) // Will process later so we can send an estimation of storedCdr to http server
} else {
return nil, fmt.Errorf("Unsupported field type: %s", cdrFldCfg.Type)
}
if err := populateStoredCdrField(storedCdr, cdrFldCfg.CdrFieldId, fieldVal, self.timezone); err != nil {
return nil, err
}
}
storedCdr.CgrId = utils.Sha1(storedCdr.AccId, storedCdr.SetupTime.UTC().String())
if storedCdr.TOR == utils.DATA && self.duMultiplyFactors[cfgIdx] != 0 {
storedCdr.Usage = time.Duration(float64(storedCdr.Usage.Nanoseconds()) * self.duMultiplyFactors[cfgIdx])
}
for _, httpFieldCfg := range lazyHttpFields { // Lazy process the http fields
var outValByte []byte
var fieldVal, httpAddr string
for _, rsrFld := range httpFieldCfg.Value {
httpAddr += rsrFld.ParseValue("")
}
if outValByte, err = utils.HttpJsonPost(httpAddr, self.httpSkipTlsCheck, storedCdr); err != nil && httpFieldCfg.Mandatory {
return nil, err
} else {
fieldVal = string(outValByte)
if len(fieldVal) == 0 && httpFieldCfg.Mandatory {
return nil, fmt.Errorf("MandatoryIeMissing: Empty result for http_post field: %s", httpFieldCfg.Tag)
}
if err := populateStoredCdrField(storedCdr, httpFieldCfg.CdrFieldId, fieldVal, self.timezone); err != nil {
return nil, err
}
}
}
return storedCdr, nil
}

151
cdrc/csv_test.go Normal file
View File

@@ -0,0 +1,151 @@
/*
Real-time Charging System 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 cdrc
import (
"reflect"
"testing"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
func TestCsvRecordForkCdr(t *testing.T) {
cgrConfig, _ := config.NewDefaultCGRConfig()
cdrcConfig := cgrConfig.CdrcProfiles["/var/log/cgrates/cdrc/in"][utils.META_DEFAULT]
cdrcConfig.ContentFields = append(cdrcConfig.ContentFields, &config.CfgCdrField{Tag: "SupplierTest", Type: utils.CDRFIELD, CdrFieldId: "supplier", Value: []*utils.RSRField{&utils.RSRField{Id: "14"}}})
cdrcConfig.ContentFields = append(cdrcConfig.ContentFields, &config.CfgCdrField{Tag: "DisconnectCauseTest", Type: utils.CDRFIELD, CdrFieldId: utils.DISCONNECT_CAUSE,
Value: []*utils.RSRField{&utils.RSRField{Id: "16"}}})
csvProcessor := &CsvRecordsProcessor{cdrFormat: CSV, cdrSourceIds: []string{"TEST_CDRC"}, cdrFields: [][]*config.CfgCdrField{cdrcConfig.ContentFields}}
cdrRow := []string{"firstField", "secondField"}
_, err := csvProcessor.recordToStoredCdr(cdrRow, 0)
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, 0)
if err != nil {
t.Error("Failed to parse CDR in rated cdr", err)
}
expectedCdr := &engine.StoredCdr{
CgrId: utils.Sha1(cdrRow[3], time.Date(2013, 2, 3, 19, 50, 0, 0, time.UTC).String()),
TOR: cdrRow[2],
AccId: cdrRow[3],
CdrHost: "0.0.0.0", // Got it over internal interface
CdrSource: "TEST_CDRC",
ReqType: cdrRow[4],
Direction: cdrRow[5],
Tenant: cdrRow[6],
Category: cdrRow[7],
Account: cdrRow[8],
Subject: cdrRow[9],
Destination: cdrRow[10],
SetupTime: time.Date(2013, 2, 3, 19, 50, 0, 0, time.UTC),
AnswerTime: time.Date(2013, 2, 3, 19, 54, 0, 0, time.UTC),
Usage: time.Duration(62) * time.Second,
Supplier: "supplier1",
DisconnectCause: "NORMAL_DISCONNECT",
ExtraFields: map[string]string{},
Cost: -1,
}
if !reflect.DeepEqual(expectedCdr, rtCdr) {
t.Errorf("Expected: \n%v, \nreceived: \n%v", expectedCdr, rtCdr)
}
}
func TestCsvDataMultiplyFactor(t *testing.T) {
cdrFields := []*config.CfgCdrField{&config.CfgCdrField{Tag: "TORField", Type: utils.CDRFIELD, CdrFieldId: "tor", Value: []*utils.RSRField{&utils.RSRField{Id: "0"}}},
&config.CfgCdrField{Tag: "UsageField", Type: utils.CDRFIELD, CdrFieldId: "usage", Value: []*utils.RSRField{&utils.RSRField{Id: "1"}}}}
csvProcessor := &CsvRecordsProcessor{cdrFormat: CSV, cdrSourceIds: []string{"TEST_CDRC"}, duMultiplyFactors: []float64{0}, cdrFields: [][]*config.CfgCdrField{cdrFields}}
cdrRow := []string{"*data", "1"}
rtCdr, err := csvProcessor.recordToStoredCdr(cdrRow, 0)
if err != nil {
t.Error("Failed to parse CDR in rated cdr", err)
}
var sTime time.Time
expectedCdr := &engine.StoredCdr{
CgrId: utils.Sha1("", sTime.String()),
TOR: cdrRow[0],
CdrHost: "0.0.0.0",
CdrSource: "TEST_CDRC",
Usage: time.Duration(1) * time.Second,
ExtraFields: map[string]string{},
Cost: -1,
}
if !reflect.DeepEqual(expectedCdr, rtCdr) {
t.Errorf("Expected: \n%v, \nreceived: \n%v", expectedCdr, rtCdr)
}
csvProcessor.duMultiplyFactors = []float64{1024}
expectedCdr = &engine.StoredCdr{
CgrId: utils.Sha1("", sTime.String()),
TOR: cdrRow[0],
CdrHost: "0.0.0.0",
CdrSource: "TEST_CDRC",
Usage: time.Duration(1024) * time.Second,
ExtraFields: map[string]string{},
Cost: -1,
}
if rtCdr, _ := csvProcessor.recordToStoredCdr(cdrRow, 0); !reflect.DeepEqual(expectedCdr, rtCdr) {
t.Errorf("Expected: \n%v, \nreceived: \n%v", expectedCdr, rtCdr)
}
cdrRow = []string{"*voice", "1"}
expectedCdr = &engine.StoredCdr{
CgrId: utils.Sha1("", sTime.String()),
TOR: cdrRow[0],
CdrHost: "0.0.0.0",
CdrSource: "TEST_CDRC",
Usage: time.Duration(1) * time.Second,
ExtraFields: map[string]string{},
Cost: -1,
}
if rtCdr, _ := csvProcessor.recordToStoredCdr(cdrRow, 0); !reflect.DeepEqual(expectedCdr, rtCdr) {
t.Errorf("Expected: \n%v, \nreceived: \n%v", expectedCdr, rtCdr)
}
}
func TestCsvPairToRecord(t *testing.T) {
eRecord := []string{"INVITE", "2daec40c", "548625ac", "dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:0", "200", "OK", "1436454408", "*prepaid", "1001", "1002", "", "3401:2069362475", "2"}
invPr := &PartialFlatstoreRecord{Method: "INVITE", Timestamp: time.Date(2015, 7, 9, 15, 6, 48, 0, time.UTC),
Values: []string{"INVITE", "2daec40c", "548625ac", "dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:0", "200", "OK", "1436454408", "*prepaid", "1001", "1002", "", "3401:2069362475"}}
byePr := &PartialFlatstoreRecord{Method: "BYE", Timestamp: time.Date(2015, 7, 9, 15, 6, 50, 0, time.UTC),
Values: []string{"BYE", "2daec40c", "548625ac", "dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:0", "200", "OK", "1436454410", "", "", "", "", "3401:2069362475"}}
if rec, err := pairToRecord(invPr, byePr); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eRecord, rec) {
t.Errorf("Expected: %+v, received: %+v", eRecord, rec)
}
if rec, err := pairToRecord(byePr, invPr); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eRecord, rec) {
t.Errorf("Expected: %+v, received: %+v", eRecord, rec)
}
if _, err := pairToRecord(byePr, byePr); err == nil || err.Error() != "MISSING_INVITE" {
t.Error(err)
}
if _, err := pairToRecord(invPr, invPr); err == nil || err.Error() != "MISSING_BYE" {
t.Error(err)
}
byePr.Values = []string{"BYE", "2daec40c", "548625ac", "dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:0", "200", "OK", "1436454410", "", "", "", "3401:2069362475"} // Took one value out
if _, err := pairToRecord(invPr, byePr); err == nil || err.Error() != "INCONSISTENT_VALUES_LENGTH" {
t.Error(err)
}
}

View File

@@ -0,0 +1,161 @@
/*
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 cdrc
import (
"io/ioutil"
"net/rpc"
"net/rpc/jsonrpc"
"os"
"path"
"testing"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
)
var flatstoreCfgPath string
var flatstoreCfg *config.CGRConfig
var flatstoreRpc *rpc.Client
var flatstoreCdrcCfg *config.CdrcConfig
var fullSuccessfull = `INVITE|2daec40c|548625ac|dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:0|200|OK|1436454408|*prepaid|1001|1002||3401:2069362475
BYE|2daec40c|548625ac|dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:0|200|OK|1436454410|||||3401:2069362475
INVITE|f9d3d5c3|c863a6e3|214d8f52b566e33a9349b184e72a4cca@0:0:0:0:0:0:0:0|200|OK|1436454647|*postpaid|1002|1001||1877:893549741
BYE|f9d3d5c3|c863a6e3|214d8f52b566e33a9349b184e72a4cca@0:0:0:0:0:0:0:0|200|OK|1436454651|||||1877:893549741
INVITE|36e39a5|42d996f9|3a63321dd3b325eec688dc2aefb6ac2d@0:0:0:0:0:0:0:0|200|OK|1436454657|*prepaid|1001|1002||2407:1884881533
BYE|36e39a5|42d996f9|3a63321dd3b325eec688dc2aefb6ac2d@0:0:0:0:0:0:0:0|200|OK|1436454661|||||2407:1884881533
INVITE|3111f3c9|49ca4c42|a58ebaae40d08d6757d8424fb09c4c54@0:0:0:0:0:0:0:0|200|OK|1436454690|*prepaid|1001|1002||3099:1909036290
BYE|3111f3c9|49ca4c42|a58ebaae40d08d6757d8424fb09c4c54@0:0:0:0:0:0:0:0|200|OK|1436454692|||||3099:1909036290
`
var fullMissed = `INVITE|ef6c6256|da501581|0bfdd176d1b93e7df3de5c6f4873ee04@0:0:0:0:0:0:0:0|487|Request Terminated|1436454643|*prepaid|1001|1002||1224:339382783
INVITE|7905e511||81880da80a94bda81b425b09009e055c@0:0:0:0:0:0:0:0|404|Not Found|1436454668|*prepaid|1001|1002||1980:1216490844
INVITE|324cb497|d4af7023|8deaadf2ae9a17809a391f05af31afb0@0:0:0:0:0:0:0:0|486|Busy here|1436454687|*postpaid|1002|1001||474:130115066`
var part1 = `BYE|f9d3d5c3|c863a6e3|214d8f52b566e33a9349b184e72a4ccb@0:0:0:0:0:0:0:0|200|OK|1436454651|||||1877:893549742
`
var part2 = `INVITE|f9d3d5c3|c863a6e3|214d8f52b566e33a9349b184e72a4ccb@0:0:0:0:0:0:0:0|200|OK|1436454647|*postpaid|1002|1003||1877:893549742
INVITE|2daec40c|548625ac|dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:0|200|OK|1436454408|*prepaid|1001|1002||3401:2069362475`
func TestFlatstoreLclInitCfg(t *testing.T) {
if !*testLocal {
return
}
var err error
flatstoreCfgPath = path.Join(*dataDir, "conf", "samples", "cdrcflatstore")
if flatstoreCfg, err = config.NewCGRConfigFromFolder(flatstoreCfgPath); err != nil {
t.Fatal("Got config error: ", err.Error())
}
}
// InitDb so we can rely on count
func TestFlatstoreLclInitCdrDb(t *testing.T) {
if !*testLocal {
return
}
if err := engine.InitStorDb(flatstoreCfg); err != nil {
t.Fatal(err)
}
}
// Creates cdr files and moves them into processing folder
func TestFlatstoreLclCreateCdrFiles(t *testing.T) {
if !*testLocal {
return
}
if flatstoreCfg == nil {
t.Fatal("Empty default cdrc configuration")
}
flatstoreCdrcCfg = flatstoreCfg.CdrcProfiles["/tmp/cgr_flatstore/cdrc/in"]["FLATSTORE"]
if err := os.RemoveAll(flatstoreCdrcCfg.CdrInDir); err != nil {
t.Fatal("Error removing folder: ", flatstoreCdrcCfg.CdrInDir, err)
}
if err := os.MkdirAll(flatstoreCdrcCfg.CdrInDir, 0755); err != nil {
t.Fatal("Error creating folder: ", flatstoreCdrcCfg.CdrInDir, err)
}
if err := os.RemoveAll(flatstoreCdrcCfg.CdrOutDir); err != nil {
t.Fatal("Error removing folder: ", flatstoreCdrcCfg.CdrOutDir, err)
}
if err := os.MkdirAll(flatstoreCdrcCfg.CdrOutDir, 0755); err != nil {
t.Fatal("Error creating folder: ", flatstoreCdrcCfg.CdrOutDir, err)
}
}
func TestFlatstoreLclStartEngine(t *testing.T) {
if !*testLocal {
return
}
if _, err := engine.StopStartEngine(flatstoreCfgPath, *waitRater); err != nil {
t.Fatal(err)
}
}
// Connect rpc client to rater
func TestFlatstoreLclRpcConn(t *testing.T) {
if !*testLocal {
return
}
var err error
flatstoreRpc, err = jsonrpc.Dial("tcp", flatstoreCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
if err != nil {
t.Fatal("Could not connect to rater: ", err.Error())
}
}
func TestFlatstoreLclProcessFiles(t *testing.T) {
if !*testLocal {
return
}
if err := ioutil.WriteFile(path.Join("/tmp", "acc_1.log"), []byte(fullSuccessfull), 0644); err != nil {
t.Fatal(err.Error)
}
if err := ioutil.WriteFile(path.Join("/tmp", "missed_calls_1.log"), []byte(fullMissed), 0644); err != nil {
t.Fatal(err.Error)
}
if err := ioutil.WriteFile(path.Join("/tmp", "acc_2.log"), []byte(part1), 0644); err != nil {
t.Fatal(err.Error)
}
if err := ioutil.WriteFile(path.Join("/tmp", "acc_3.log"), []byte(part2), 0644); err != nil {
t.Fatal(err.Error)
}
//Rename(oldpath, newpath string)
for _, fileName := range []string{"acc_1.log", "missed_calls_1.log", "acc_2.log", "acc_3.log"} {
if err := os.Rename(path.Join("/tmp", fileName), path.Join(flatstoreCdrcCfg.CdrInDir, fileName)); err != nil {
t.Fatal(err)
}
}
time.Sleep(time.Duration(2) * time.Second) // Give time for processing to happen and the .unparired file to be written
filesInDir, _ := ioutil.ReadDir(flatstoreCdrcCfg.CdrInDir)
if len(filesInDir) != 0 {
t.Errorf("Files in cdrcInDir: %+v", filesInDir)
}
filesOutDir, _ := ioutil.ReadDir(flatstoreCdrcCfg.CdrOutDir)
if len(filesOutDir) != 5 {
t.Errorf("In CdrcOutDir, expecting 5 files, got: %d", len(filesOutDir))
}
ePartContent := "INVITE|2daec40c|548625ac|dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:0|200|OK|1436454408|*prepaid|1001|1002||3401:2069362475\n"
if partContent, err := ioutil.ReadFile(path.Join(flatstoreCdrcCfg.CdrOutDir, "acc_3.log.unpaired")); err != nil {
t.Error(err)
} else if ePartContent != string(partContent) {
t.Errorf("Expecting: %s, received: %s", ePartContent, string(partContent))
}
}

246
cdrc/fwv.go Normal file
View File

@@ -0,0 +1,246 @@
/*
Real-time Charging System 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 cdrc
import (
"bufio"
"fmt"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
"io"
"net/http"
"os"
"strconv"
"strings"
"time"
)
func fwvValue(cdrLine string, indexStart, width int, padding string) string {
rawVal := cdrLine[indexStart : indexStart+width]
switch padding {
case "left":
rawVal = strings.TrimLeft(rawVal, " ")
case "right":
rawVal = strings.TrimRight(rawVal, " ")
case "zeroleft":
rawVal = strings.TrimLeft(rawVal, "0 ")
case "zeroright":
rawVal = strings.TrimRight(rawVal, "0 ")
}
return rawVal
}
func NewFwvRecordsProcessor(file *os.File, cdrcCfgs map[string]*config.CdrcConfig, dfltCfg *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
cdrcCfgs map[string]*config.CdrcConfig
dfltCfg *config.CdrcConfig // General parameters
httpClient *http.Client
httpSkipTlsCheck bool
timezone string
lineLen int64 // Length of the line in the file
offset int64 // Index of the next byte to process
trailerOffset int64 // Index where trailer starts, to be used as boundary when reading cdrs
headerCdr *engine.StoredCdr // Cache here the general purpose stored CDR
}
// Sets the line length based on first line, sets offset back to initial after reading
func (self *FwvRecordsProcessor) setLineLen() error {
rdr := bufio.NewReader(self.file)
readBytes, err := rdr.ReadBytes('\n')
if err != nil {
return err
}
self.lineLen = int64(len(readBytes))
if _, err := self.file.Seek(0, 0); err != nil {
return err
}
return nil
}
func (self *FwvRecordsProcessor) ProcessNextRecord() ([]*engine.StoredCdr, error) {
defer func() { self.offset += self.lineLen }() // Schedule increasing the offset once we are out from processing the record
if self.offset == 0 { // First time, set the necessary offsets
if err := self.setLineLen(); err != nil {
engine.Logger.Err(fmt.Sprintf("<Cdrc> Row 0, error: cannot set lineLen: %s", err.Error()))
return nil, io.EOF
}
if len(self.dfltCfg.TrailerFields) != 0 {
if fi, err := self.file.Stat(); err != nil {
engine.Logger.Err(fmt.Sprintf("<Cdrc> Row 0, error: cannot get file stats: %s", err.Error()))
return nil, err
} else {
self.trailerOffset = fi.Size() - self.lineLen
}
}
if len(self.dfltCfg.HeaderFields) != 0 { // ToDo: Process here the header fields
if err := self.processHeader(); err != nil {
engine.Logger.Err(fmt.Sprintf("<Cdrc> Row 0, error reading header: %s", err.Error()))
return nil, io.EOF
}
return nil, nil
}
}
recordCdrs := make([]*engine.StoredCdr, 0) // More CDRs based on the number of filters and field templates
if self.trailerOffset != 0 && self.offset >= self.trailerOffset {
if err := self.processTrailer(); err != nil && err != io.EOF {
engine.Logger.Err(fmt.Sprintf("<Cdrc> Read trailer error: %s ", err.Error()))
}
return nil, io.EOF
}
buf := make([]byte, self.lineLen)
nRead, err := self.file.Read(buf)
if err != nil {
return nil, err
} else if nRead != len(buf) {
engine.Logger.Err(fmt.Sprintf("<Cdrc> Could not read complete line, have instead: %s", string(buf)))
return nil, io.EOF
}
record := string(buf)
for cfgKey := range self.cdrcCfgs {
if passes := self.recordPassesCfgFilter(record, cfgKey); !passes {
continue
}
if storedCdr, err := self.recordToStoredCdr(record, cfgKey); err != nil {
return nil, fmt.Errorf("Failed converting to StoredCdr, error: %s", err.Error())
} else {
recordCdrs = append(recordCdrs, storedCdr)
}
}
return recordCdrs, nil
}
func (self *FwvRecordsProcessor) recordPassesCfgFilter(record, configKey string) bool {
filterPasses := true
for _, rsrFilter := range self.cdrcCfgs[configKey].CdrFilter {
if rsrFilter == nil { // Nil filter does not need to match anything
continue
}
if cfgFieldIdx, _ := strconv.Atoi(rsrFilter.Id); len(record) <= cfgFieldIdx {
fmt.Errorf("Ignoring record: %v - cannot compile filter %+v", record, rsrFilter)
return false
} else if !rsrFilter.FilterPasses(record[cfgFieldIdx:]) {
filterPasses = false
break
}
}
return filterPasses
}
// Converts a record (header or normal) to StoredCdr
func (self *FwvRecordsProcessor) recordToStoredCdr(record string, cfgKey string) (*engine.StoredCdr, error) {
var err error
var lazyHttpFields []*config.CfgCdrField
var cfgFields []*config.CfgCdrField
var duMultiplyFactor float64
var storedCdr *engine.StoredCdr
if self.headerCdr != nil { // Clone the header CDR so we can use it as base to future processing (inherit fields defined there)
storedCdr = self.headerCdr.Clone()
} else {
storedCdr = &engine.StoredCdr{CdrHost: "0.0.0.0", ExtraFields: make(map[string]string), Cost: -1}
}
if cfgKey == "*header" {
cfgFields = self.dfltCfg.HeaderFields
storedCdr.CdrSource = self.dfltCfg.CdrSourceId
duMultiplyFactor = self.dfltCfg.DataUsageMultiplyFactor
} else {
cfgFields = self.cdrcCfgs[cfgKey].ContentFields
storedCdr.CdrSource = self.cdrcCfgs[cfgKey].CdrSourceId
duMultiplyFactor = self.cdrcCfgs[cfgKey].DataUsageMultiplyFactor
}
for _, cdrFldCfg := range cfgFields {
var fieldVal string
switch cdrFldCfg.Type {
case utils.CDRFIELD:
for _, cfgFieldRSR := range cdrFldCfg.Value {
if cfgFieldRSR.IsStatic() {
fieldVal += cfgFieldRSR.ParseValue("")
} else { // Dynamic value extracted using index
if cfgFieldIdx, _ := strconv.Atoi(cfgFieldRSR.Id); len(record) <= cfgFieldIdx {
return nil, fmt.Errorf("Ignoring record: %v - cannot extract field %s", record, cdrFldCfg.Tag)
} else {
fieldVal += cfgFieldRSR.ParseValue(fwvValue(record, cfgFieldIdx, cdrFldCfg.Width, cdrFldCfg.Padding))
}
}
}
case utils.HTTP_POST:
lazyHttpFields = append(lazyHttpFields, cdrFldCfg) // Will process later so we can send an estimation of storedCdr to http server
default:
//return nil, fmt.Errorf("Unsupported field type: %s", cdrFldCfg.Type)
continue // Don't do anything for unsupported fields
}
if err := populateStoredCdrField(storedCdr, cdrFldCfg.CdrFieldId, fieldVal, self.timezone); err != nil {
return nil, err
}
}
if storedCdr.CgrId == "" && storedCdr.AccId != "" && cfgKey != "*header" {
storedCdr.CgrId = utils.Sha1(storedCdr.AccId, storedCdr.SetupTime.UTC().String())
}
if storedCdr.TOR == utils.DATA && duMultiplyFactor != 0 {
storedCdr.Usage = time.Duration(float64(storedCdr.Usage.Nanoseconds()) * duMultiplyFactor)
}
for _, httpFieldCfg := range lazyHttpFields { // Lazy process the http fields
var outValByte []byte
var fieldVal, httpAddr string
for _, rsrFld := range httpFieldCfg.Value {
httpAddr += rsrFld.ParseValue("")
}
if outValByte, err = utils.HttpJsonPost(httpAddr, self.httpSkipTlsCheck, storedCdr); err != nil && httpFieldCfg.Mandatory {
return nil, err
} else {
fieldVal = string(outValByte)
if len(fieldVal) == 0 && httpFieldCfg.Mandatory {
return nil, fmt.Errorf("MandatoryIeMissing: Empty result for http_post field: %s", httpFieldCfg.Tag)
}
if err := populateStoredCdrField(storedCdr, httpFieldCfg.CdrFieldId, fieldVal, self.timezone); err != nil {
return nil, err
}
}
}
return storedCdr, nil
}
func (self *FwvRecordsProcessor) processHeader() error {
buf := make([]byte, self.lineLen)
if nRead, err := self.file.Read(buf); err != nil {
return err
} else if nRead != len(buf) {
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 {
return err
}
return nil
}
func (self *FwvRecordsProcessor) processTrailer() error {
buf := make([]byte, self.lineLen)
if nRead, err := self.file.ReadAt(buf, self.trailerOffset); err != nil {
return err
} else if nRead != len(buf) {
return fmt.Errorf("In trailer, line len: %d, have read: %d", self.lineLen, nRead)
}
//engine.Logger.Debug(fmt.Sprintf("Have read trailer: <%q>", string(buf)))
return nil
}

150
cdrc/fwv_local_test.go Normal file
View File

@@ -0,0 +1,150 @@
/*
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 cdrc
import (
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"io/ioutil"
"net/rpc"
"net/rpc/jsonrpc"
"os"
"path"
"testing"
"time"
)
var fwvCfgPath string
var fwvCfg *config.CGRConfig
var fwvRpc *rpc.Client
var fwvCdrcCfg *config.CdrcConfig
var FW_CDR_FILE1 = `HDR0001DDB ABC Some Connect A.B. DDB-Some-10022-20120711-309.CDR 00030920120711100255
CDR0000010 0 20120708181506000123451234 0040123123120 004 000018009980010001ISDN ABC 10Buiten uw regio EHV 00000009190000000009
CDR0000020 0 20120708190945000123451234 0040123123120 004 000016009980010001ISDN ABC 10Buiten uw regio EHV 00000009190000000009
CDR0000030 0 20120708191009000123451234 0040123123120 004 000020009980010001ISDN ABC 10Buiten uw regio EHV 00000009190000000009
CDR0000040 0 20120708231043000123451234 0040123123120 004 000011009980010001ISDN ABC 10Buiten uw regio EHV 00000009190000000009
CDR0000050 0 20120709122216000123451235 004212 004 000217009980010001ISDN ABC 10Buiten uw regio HMR 00000000190000000000
CDR0000060 0 20120709130542000123451236 0012323453 004 000019009980010001ISDN ABC 35Sterdiensten AP 00000000190000000000
CDR0000070 0 20120709140032000123451237 0040012323453100 001 000050009980030001ISDN ABD 20Internationaal NLB 00000000190000000000
CDR0000080 0 20120709140142000123451237 0040012323453100 001 000050009980030001ISDN ABD 20Internationaal NLB 00000000190000000000
CDR0000090 0 20120709150305000123451237 0040012323453100 001 000050009980030001ISDN ABD 20Internationaal NLB 00000000190000000000
CDR0000100 0 20120709150414000123451237 0040012323453100 001 000057009980030001ISDN ABD 20Internationaal NLB 00000000190000000000
CDR0000110 0 20120709150531000123451237 0040012323453100 001 000059009980030001ISDN ABD 20Internationaal NLB 00000000190000000000
CDR0000120 0 20120709150635000123451237 0040012323453100 001 000050009980030001ISDN ABD 20Internationaal NLB 00000000190000000000
CDR0000130 0 20120709151756000123451237 0040012323453100 001 000050009980030001ISDN ABD 20Internationaal NLB 00000000190000000000
CDR0000140 0 20120709154549000123451237 0040012323453100 001 000052009980030001ISDN ABD 20Internationaal NLB 00000000190000000000
CDR0000150 0 20120709154701000123451237 0040012323453100 001 000121009980030001ISDN ABD 20Internationaal NLB 00000000190000000000
CDR0000160 0 20120709154842000123451237 0040012323453100 001 000055009980030001ISDN ABD 20Internationaal NLB 00000000190000000000
CDR0000170 0 20120709154956000123451237 0040012323453100 001 000115009980030001ISDN ABD 20Internationaal NLB 00000000190000000000
CDR0000180 0 20120709155131000123451237 0040012323453100 001 000059009980030001ISDN ABD 20Internationaal NLB 00000000190000000000
CDR0000190 0 20120709155236000123451237 0040012323453100 001 000050009980030001ISDN ABD 20Internationaal NLB 00000000190000000000
CDR0000200 0 20120709160309000123451237 0040012323453100 001 000100009980030001ISDN ABD 20Internationaal NLB 00000000190000000000
CDR0000210 0 20120709160415000123451237 0040012323453100 001 000050009980030001ISDN ABD 20Internationaal NLB 00000000190000000000
CDR0000220 0 20120709161739000123451237 0040012323453100 001 000058009980030001ISDN ABD 20Internationaal NLB 00000000190000000000
CDR0000230 0 20120709170356000123123459 0040123234531 004 000012002760010001ISDN 276 10Buiten uw regio TB 00000009190000000009
CDR0000240 0 20120709181036000123123450 0012323453 004 000042009980010001ISDN ABC 05Binnen uw regio AP 00000010190000000010
CDR0000250 0 20120709191245000123123458 0040123232350 004 000012002760000001PSTN 276 10Buiten uw regio TB 00000009190000000009
CDR0000260 0 20120709202324000123123459 0040123234531 004 000011002760010001ISDN 276 10Buiten uw regio TB 00000009190000000009
CDR0000270 0 20120709211756000123451237 0040012323453100 001 000051009980030001ISDN ABD 20Internationaal NLB 00000000190000000000
CDR0000280 0 20120709211852000123451237 0040012323453100 001 000050009980030001ISDN ABD 20Internationaal NLB 00000000190000000000
CDR0000290 0 20120709212904000123123458 0040123232350 004 000012002760000001PSTN 276 10Buiten uw regio TB 00000009190000000009
CDR0000300 0 20120709073707000123123459 0040123234531 004 000012002760010001ISDN 276 10Buiten uw regio TB 00000009190000000009
CDR0000310 0 20120709085451000123451237 0040012323453100 001 000744009980030001ISDN ABD 20Internationaal NLB 00000000190000000000
CDR0000320 0 20120709091756000123451237 0040012323453100 001 000050009980030001ISDN ABD 20Internationaal NLB 00000000190000000000
CDR0000330 0 20120710070434000123123458 0040123232350 004 000012002760000001PSTN 276 10Buiten uw regio TB 00000009190000000009
TRL0001DDB ABC Some Connect A.B. DDB-Some-10022-20120711-309.CDR 0003090000003300000030550000000001000000000100Y
`
func TestFwvLclInitCfg(t *testing.T) {
if !*testLocal {
return
}
var err error
fwvCfgPath = path.Join(*dataDir, "conf", "samples", "cdrcfwv")
if fwvCfg, err = config.NewCGRConfigFromFolder(fwvCfgPath); err != nil {
t.Fatal("Got config error: ", err.Error())
}
}
// Creates cdr files and moves them into processing folder
func TestFwvLclCreateCdrFiles(t *testing.T) {
if !*testLocal {
return
}
if fwvCfg == nil {
t.Fatal("Empty default cdrc configuration")
}
fwvCdrcCfg = fwvCfg.CdrcProfiles["/tmp/cgr_fwv/cdrc/in"]["FWV1"]
if err := os.RemoveAll(fwvCdrcCfg.CdrInDir); err != nil {
t.Fatal("Error removing folder: ", fwvCdrcCfg.CdrInDir, err)
}
if err := os.MkdirAll(fwvCdrcCfg.CdrInDir, 0755); err != nil {
t.Fatal("Error creating folder: ", fwvCdrcCfg.CdrInDir, err)
}
if err := os.RemoveAll(fwvCdrcCfg.CdrOutDir); err != nil {
t.Fatal("Error removing folder: ", fwvCdrcCfg.CdrOutDir, err)
}
if err := os.MkdirAll(fwvCdrcCfg.CdrOutDir, 0755); err != nil {
t.Fatal("Error creating folder: ", fwvCdrcCfg.CdrOutDir, err)
}
}
func TestFwvLclStartEngine(t *testing.T) {
if !*testLocal {
return
}
if _, err := engine.StopStartEngine(fwvCfgPath, *waitRater); err != nil {
t.Fatal(err)
}
}
// Connect rpc client to rater
func TestFwvLclRpcConn(t *testing.T) {
if !*testLocal {
return
}
var err error
fwvRpc, err = jsonrpc.Dial("tcp", fwvCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
if err != nil {
t.Fatal("Could not connect to rater: ", err.Error())
}
}
func TestFwvLclProcessFiles(t *testing.T) {
if !*testLocal {
return
}
fileName := "test1.fwv"
if err := ioutil.WriteFile(path.Join("/tmp", fileName), []byte(FW_CDR_FILE1), 0644); err != nil {
t.Fatal(err.Error)
}
if err := os.Rename(path.Join("/tmp", fileName), path.Join(fwvCdrcCfg.CdrInDir, fileName)); err != nil {
t.Fatal(err)
}
time.Sleep(time.Duration(1) * time.Second)
filesInDir, _ := ioutil.ReadDir(fwvCdrcCfg.CdrInDir)
if len(filesInDir) != 0 {
t.Errorf("Files in cdrcInDir: %d", len(filesInDir))
}
filesOutDir, _ := ioutil.ReadDir(fwvCdrcCfg.CdrOutDir)
if len(filesOutDir) != 1 {
t.Errorf("In CdrcOutDir, expecting 1 files, got: %d", len(filesOutDir))
}
}

55
cdrc/fwv_test.go Normal file
View File

@@ -0,0 +1,55 @@
/*
Real-time Charging System 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 cdrc
import (
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/utils"
"testing"
)
func TestFwvValue(t *testing.T) {
cdrLine := "CDR0000010 0 20120708181506000123451234 0040123123120 004 000018009980010001ISDN ABC 10Buiten uw regio EHV 00000009190000000009"
if val := fwvValue(cdrLine, 30, 19, "right"); val != "0123451234" {
t.Errorf("Received: <%s>", val)
}
if val := fwvValue(cdrLine, 14, 16, "right"); val != "2012070818150600" { // SetupTime
t.Errorf("Received: <%s>", val)
}
if val := fwvValue(cdrLine, 127, 8, "right"); val != "00001800" { // Usage
t.Errorf("Received: <%s>", val)
}
cdrLine = "HDR0001DDB ABC Some Connect A.B. DDB-Some-10022-20120711-309.CDR 00030920120711100255 "
if val := fwvValue(cdrLine, 135, 6, "zeroleft"); val != "309" {
t.Errorf("Received: <%s>", val)
}
}
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.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 {
t.Error("Not passes filter")
}
}

534
cdre/cdrexporter.go Normal file
View File

@@ -0,0 +1,534 @@
/*
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 cdre
import (
"encoding/csv"
"encoding/json"
"fmt"
"io"
"os"
"strconv"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
const (
COST_DETAILS = "cost_details"
DATETIME = "datetime"
META_EXPORTID = "*export_id"
META_TIMENOW = "*time_now"
META_FIRSTCDRATIME = "*first_cdr_atime"
META_LASTCDRATIME = "*last_cdr_atime"
META_NRCDRS = "*cdrs_number"
META_DURCDRS = "*cdrs_duration"
META_SMSUSAGE = "*sms_usage"
META_GENERICUSAGE = "*generic_usage"
META_DATAUSAGE = "*data_usage"
META_COSTCDRS = "*cdrs_cost"
META_MASKDESTINATION = "*mask_destination"
META_FORMATCOST = "*format_cost"
)
var err error
func NewCdrExporter(cdrs []*engine.StoredCdr, cdrDb engine.CdrStorage, exportTpl *config.CdreConfig, cdrFormat string, fieldSeparator rune, exportId string,
dataUsageMultiplyFactor, smsUsageMultiplyFactor, genericUsageMultiplyFactor, costMultiplyFactor float64,
costShiftDigits, roundDecimals, cgrPrecision int, maskDestId string, maskLen int, httpSkipTlsCheck bool, timezone string) (*CdrExporter, error) {
if len(cdrs) == 0 { // Nothing to export
return nil, nil
}
cdre := &CdrExporter{
cdrs: cdrs,
cdrDb: cdrDb,
exportTemplate: exportTpl,
cdrFormat: cdrFormat,
fieldSeparator: fieldSeparator,
exportId: exportId,
dataUsageMultiplyFactor: dataUsageMultiplyFactor,
smsUsageMultiplyFactor: smsUsageMultiplyFactor,
costMultiplyFactor: costMultiplyFactor,
costShiftDigits: costShiftDigits,
roundDecimals: roundDecimals,
cgrPrecision: cgrPrecision,
maskDestId: maskDestId,
httpSkipTlsCheck: httpSkipTlsCheck,
timezone: timezone,
maskLen: maskLen,
negativeExports: make(map[string]string),
}
if err := cdre.processCdrs(); err != nil {
return nil, err
}
return cdre, nil
}
type CdrExporter struct {
cdrs []*engine.StoredCdr
cdrDb engine.CdrStorage // Used to extract cost_details if these are requested
exportTemplate *config.CdreConfig
cdrFormat string // csv, fwv
fieldSeparator rune
exportId string // Unique identifier or this export
dataUsageMultiplyFactor,
smsUsageMultiplyFactor, // Multiply the SMS usage (eg: some billing systems billing them as minutes)
genericUsageMultiplyFactor,
costMultiplyFactor float64
costShiftDigits, roundDecimals, cgrPrecision int
maskDestId string
maskLen int
httpSkipTlsCheck bool
timezone string
header, trailer []string // Header and Trailer fields
content [][]string // Rows of cdr fields
firstCdrATime, lastCdrATime time.Time
numberOfRecords int
totalDuration, totalDataUsage, totalSmsUsage, totalGenericUsage time.Duration
totalCost float64
firstExpOrderId, lastExpOrderId int64
positiveExports []string // CGRIds of successfully exported CDRs
negativeExports map[string]string // CgrIds of failed exports
}
// 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)
if err != nil {
return "", err
} else if cc == nil {
return "", nil
}
ccJson, _ := json.Marshal(cc)
return string(ccJson), nil
}
func (cdre *CdrExporter) getCombimedCdrFieldVal(processedCdr *engine.StoredCdr, cfgCdrFld *config.CfgCdrField) (string, error) {
var combinedVal string // Will result as combination of the field values, filters must match
for _, filterRule := range cfgCdrFld.FieldFilter {
fltrPass, ftrPassValue := processedCdr.PassesFieldFilter(filterRule)
if !fltrPass {
return "", nil
}
for _, cdr := range cdre.cdrs {
if cdr.CgrId != processedCdr.CgrId {
continue // We only care about cdrs with same primary cdr behind
}
if cdr.FieldAsString(&utils.RSRField{Id: filterRule.Id}) == ftrPassValue { // First CDR with filte
for _, rsrRule := range cfgCdrFld.Value {
combinedVal += cdr.FieldAsString(rsrRule)
}
}
}
}
return combinedVal, nil
}
// Check if the destination should be masked in output
func (cdre *CdrExporter) maskedDestination(destination string) bool {
if len(cdre.maskDestId) != 0 && engine.CachedDestHasPrefix(cdre.maskDestId, destination) {
return true
}
return false
}
func (cdre *CdrExporter) getDateTimeFieldVal(cdr *engine.StoredCdr, cfgCdrFld *config.CfgCdrField) (string, error) {
if len(cfgCdrFld.Value) == 0 {
return "", nil
}
for _, fltrRl := range cfgCdrFld.FieldFilter {
if fltrPass, _ := cdr.PassesFieldFilter(fltrRl); !fltrPass {
return "", fmt.Errorf("Field: %s not matching filter rule %v", fltrRl.Id, fltrRl)
}
}
layout := cfgCdrFld.Layout
if len(layout) == 0 {
layout = time.RFC3339
}
if dtFld, err := utils.ParseTimeDetectLayout(cdr.FieldAsString(cfgCdrFld.Value[0]), cdre.timezone); err != nil { // Only one rule makes sense here
return "", err
} else {
return dtFld.Format(layout), nil
}
}
// Extracts the value specified by cfgHdr out of cdr
func (cdre *CdrExporter) cdrFieldValue(cdr *engine.StoredCdr, cfgCdrFld *config.CfgCdrField) (string, error) {
for _, fltrRl := range cfgCdrFld.FieldFilter {
if fltrPass, _ := cdr.PassesFieldFilter(fltrRl); !fltrPass {
return "", fmt.Errorf("Field: %s not matching filter rule %v", fltrRl.Id, fltrRl)
}
}
layout := cfgCdrFld.Layout
if len(layout) == 0 {
layout = time.RFC3339
}
var retVal string // Concatenate the resulting values
for _, rsrFld := range cfgCdrFld.Value {
var cdrVal string
switch rsrFld.Id {
case COST_DETAILS: // Special case when we need to further extract cost_details out of logDb
if cdr.ExtraFields[COST_DETAILS], err = cdre.getCdrCostDetails(cdr.CgrId, cdr.MediationRunId); err != nil {
return "", err
} else {
cdrVal = cdr.FieldAsString(rsrFld)
}
case utils.COST:
cdrVal = cdr.FormatCost(cdre.costShiftDigits, cdre.roundDecimals)
case utils.USAGE:
cdrVal = cdr.FormatUsage(layout)
case utils.SETUP_TIME:
cdrVal = cdr.SetupTime.Format(layout)
case utils.ANSWER_TIME: // Format time based on layout
cdrVal = cdr.AnswerTime.Format(layout)
case utils.DESTINATION:
cdrVal = cdr.FieldAsString(rsrFld)
if cdre.maskLen != -1 && cdre.maskedDestination(cdrVal) {
cdrVal = MaskDestination(cdrVal, cdre.maskLen)
}
default:
cdrVal = cdr.FieldAsString(rsrFld)
}
retVal += cdrVal
}
return retVal, nil
}
// Handle various meta functions used in header/trailer
func (cdre *CdrExporter) metaHandler(tag, arg string) (string, error) {
switch tag {
case META_EXPORTID:
return cdre.exportId, nil
case META_TIMENOW:
return time.Now().Format(arg), nil
case META_FIRSTCDRATIME:
return cdre.firstCdrATime.Format(arg), nil
case META_LASTCDRATIME:
return cdre.lastCdrATime.Format(arg), nil
case META_NRCDRS:
return strconv.Itoa(cdre.numberOfRecords), nil
case META_DURCDRS:
emulatedCdr := &engine.StoredCdr{TOR: utils.VOICE, Usage: cdre.totalDuration}
return emulatedCdr.FormatUsage(arg), nil
case META_SMSUSAGE:
emulatedCdr := &engine.StoredCdr{TOR: utils.SMS, Usage: cdre.totalSmsUsage}
return emulatedCdr.FormatUsage(arg), nil
case META_GENERICUSAGE:
emulatedCdr := &engine.StoredCdr{TOR: utils.GENERIC, Usage: cdre.totalGenericUsage}
return emulatedCdr.FormatUsage(arg), nil
case META_DATAUSAGE:
emulatedCdr := &engine.StoredCdr{TOR: utils.DATA, Usage: cdre.totalDataUsage}
return emulatedCdr.FormatUsage(arg), nil
case META_COSTCDRS:
return strconv.FormatFloat(utils.Round(cdre.totalCost, cdre.roundDecimals, utils.ROUNDING_MIDDLE), 'f', -1, 64), nil
case META_MASKDESTINATION:
if cdre.maskedDestination(arg) {
return "1", nil
}
return "0", nil
default:
return "", fmt.Errorf("Unsupported METATAG: %s", tag)
}
}
// Compose and cache the header
func (cdre *CdrExporter) composeHeader() error {
for _, cfgFld := range cdre.exportTemplate.HeaderFields {
var outVal string
switch cfgFld.Type {
case utils.FILLER:
outVal = cfgFld.Value.Id()
cfgFld.Padding = "right"
case utils.CONSTANT:
outVal = cfgFld.Value.Id()
case utils.METATAG:
outVal, err = cdre.metaHandler(cfgFld.Value.Id(), cfgFld.Layout)
default:
return fmt.Errorf("Unsupported field type: %s", cfgFld.Type)
}
if err != nil {
engine.Logger.Err(fmt.Sprintf("<CdreFw> Cannot export CDR header, field %s, error: %s", cfgFld.Tag, err.Error()))
return err
}
fmtOut := outVal
if fmtOut, err = FmtFieldWidth(outVal, cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory); err != nil {
engine.Logger.Err(fmt.Sprintf("<CdreFw> Cannot export CDR header, field %s, error: %s", cfgFld.Tag, err.Error()))
return err
}
cdre.header = append(cdre.header, fmtOut)
}
return nil
}
// Compose and cache the trailer
func (cdre *CdrExporter) composeTrailer() error {
for _, cfgFld := range cdre.exportTemplate.TrailerFields {
var outVal string
switch cfgFld.Type {
case utils.FILLER:
outVal = cfgFld.Value.Id()
cfgFld.Padding = "right"
case utils.CONSTANT:
outVal = cfgFld.Value.Id()
case utils.METATAG:
outVal, err = cdre.metaHandler(cfgFld.Value.Id(), cfgFld.Layout)
default:
return fmt.Errorf("Unsupported field type: %s", cfgFld.Type)
}
if err != nil {
engine.Logger.Err(fmt.Sprintf("<CdreFw> Cannot export CDR trailer, field: %s, error: %s", cfgFld.Tag, err.Error()))
return err
}
fmtOut := outVal
if fmtOut, err = FmtFieldWidth(outVal, cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory); err != nil {
engine.Logger.Err(fmt.Sprintf("<CdreFw> Cannot export CDR trailer, field: %s, error: %s", cfgFld.Tag, err.Error()))
return err
}
cdre.trailer = append(cdre.trailer, fmtOut)
}
return nil
}
// Write individual cdr into content buffer, build stats
func (cdre *CdrExporter) processCdr(cdr *engine.StoredCdr) error {
if cdr == nil || len(cdr.CgrId) == 0 { // We do not export empty CDRs
return nil
} else if cdr.ExtraFields == nil { // Avoid assignment in nil map if not initialized
cdr.ExtraFields = make(map[string]string)
}
// Cost multiply
if cdre.dataUsageMultiplyFactor != 0.0 && cdr.TOR == utils.DATA {
cdr.UsageMultiply(cdre.dataUsageMultiplyFactor, cdre.cgrPrecision)
} else if cdre.smsUsageMultiplyFactor != 0 && cdr.TOR == utils.SMS {
cdr.UsageMultiply(cdre.smsUsageMultiplyFactor, cdre.cgrPrecision)
} else if cdre.genericUsageMultiplyFactor != 0 && cdr.TOR == utils.GENERIC {
cdr.UsageMultiply(cdre.genericUsageMultiplyFactor, cdre.cgrPrecision)
}
if cdre.costMultiplyFactor != 0.0 {
cdr.CostMultiply(cdre.costMultiplyFactor, cdre.cgrPrecision)
}
var err error
cdrRow := make([]string, len(cdre.exportTemplate.ContentFields))
for idx, cfgFld := range cdre.exportTemplate.ContentFields {
var outVal string
switch cfgFld.Type {
case utils.FILLER:
outVal = cfgFld.Value.Id()
cfgFld.Padding = "right"
case utils.CONSTANT:
outVal = cfgFld.Value.Id()
case utils.CDRFIELD:
outVal, err = cdre.cdrFieldValue(cdr, cfgFld)
case DATETIME:
outVal, err = cdre.getDateTimeFieldVal(cdr, cfgFld)
case utils.HTTP_POST:
var outValByte []byte
httpAddr := cfgFld.Value.Id()
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 {
outVal = string(outValByte)
if len(outVal) == 0 && cfgFld.Mandatory {
err = fmt.Errorf("Empty result for http_post field: %s", cfgFld.Tag)
}
}
case utils.COMBIMED:
outVal, err = cdre.getCombimedCdrFieldVal(cdr, cfgFld)
case utils.METATAG:
if cfgFld.Value.Id() == META_MASKDESTINATION {
outVal, err = cdre.metaHandler(cfgFld.Value.Id(), cdr.FieldAsString(&utils.RSRField{Id: utils.DESTINATION}))
} else {
outVal, err = cdre.metaHandler(cfgFld.Value.Id(), cfgFld.Layout)
}
}
if err != nil {
engine.Logger.Err(fmt.Sprintf("<CdreFw> Cannot export CDR with cgrid: %s and runid: %s, error: %s", cdr.CgrId, cdr.MediationRunId, err.Error()))
return err
}
fmtOut := outVal
if fmtOut, err = FmtFieldWidth(outVal, cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory); err != nil {
engine.Logger.Err(fmt.Sprintf("<CdreFw> Cannot export CDR with cgrid: %s, runid: %s, fieldName: %s, fieldValue: %s, error: %s", cdr.CgrId, cdr.MediationRunId, cfgFld.Tag, outVal, err.Error()))
return err
}
cdrRow[idx] += fmtOut
}
if len(cdrRow) == 0 { // No CDR data, most likely no configuration fields defined
return nil
} else {
cdre.content = append(cdre.content, cdrRow)
}
// Done with writing content, compute stats here
if cdre.firstCdrATime.IsZero() || cdr.AnswerTime.Before(cdre.firstCdrATime) {
cdre.firstCdrATime = cdr.AnswerTime
}
if cdr.AnswerTime.After(cdre.lastCdrATime) {
cdre.lastCdrATime = cdr.AnswerTime
}
cdre.numberOfRecords += 1
if cdr.TOR == utils.VOICE { // Only count duration for non data cdrs
cdre.totalDuration += cdr.Usage
}
if cdr.TOR == utils.SMS { // Count usage for SMS
cdre.totalSmsUsage += cdr.Usage
}
if cdr.TOR == utils.GENERIC { // Count usage for GENERIC
cdre.totalGenericUsage += cdr.Usage
}
if cdr.TOR == utils.DATA { // Count usage for DATA
cdre.totalDataUsage += cdr.Usage
}
if cdr.Cost != -1 {
cdre.totalCost += cdr.Cost
cdre.totalCost = utils.Round(cdre.totalCost, cdre.roundDecimals, utils.ROUNDING_MIDDLE)
}
if cdre.firstExpOrderId > cdr.OrderId || cdre.firstExpOrderId == 0 {
cdre.firstExpOrderId = cdr.OrderId
}
if cdre.lastExpOrderId < cdr.OrderId {
cdre.lastExpOrderId = cdr.OrderId
}
return nil
}
// Builds header, content and trailers
func (cdre *CdrExporter) processCdrs() error {
for _, cdr := range cdre.cdrs {
if err := cdre.processCdr(cdr); err != nil {
cdre.negativeExports[cdr.CgrId] = err.Error()
} else {
cdre.positiveExports = append(cdre.positiveExports, cdr.CgrId)
}
}
// Process header and trailer after processing cdrs since the metatag functions can access stats out of built cdrs
if cdre.exportTemplate.HeaderFields != nil {
if err := cdre.composeHeader(); err != nil {
return err
}
}
if cdre.exportTemplate.TrailerFields != nil {
if err := cdre.composeTrailer(); err != nil {
return err
}
}
return nil
}
// Simple write method
func (cdre *CdrExporter) writeOut(ioWriter io.Writer) error {
if len(cdre.header) != 0 {
for _, fld := range append(cdre.header, "\n") {
if _, err := io.WriteString(ioWriter, fld); err != nil {
return err
}
}
}
for _, cdrContent := range cdre.content {
for _, cdrFld := range append(cdrContent, "\n") {
if _, err := io.WriteString(ioWriter, cdrFld); err != nil {
return err
}
}
}
if len(cdre.trailer) != 0 {
for _, fld := range append(cdre.trailer, "\n") {
if _, err := io.WriteString(ioWriter, fld); err != nil {
return err
}
}
}
return nil
}
// csvWriter specific method
func (cdre *CdrExporter) writeCsv(csvWriter *csv.Writer) error {
csvWriter.Comma = cdre.fieldSeparator
if len(cdre.header) != 0 {
if err := csvWriter.Write(cdre.header); err != nil {
return err
}
}
for _, cdrContent := range cdre.content {
if err := csvWriter.Write(cdrContent); err != nil {
return err
}
}
if len(cdre.trailer) != 0 {
if err := csvWriter.Write(cdre.trailer); err != nil {
return err
}
}
csvWriter.Flush()
return nil
}
// General method to write the content out to a file
func (cdre *CdrExporter) WriteToFile(filePath string) error {
fileOut, err := os.Create(filePath)
if err != nil {
return err
}
defer fileOut.Close()
switch cdre.cdrFormat {
case utils.DRYRUN:
return nil
case utils.CDRE_FIXED_WIDTH:
if err := cdre.writeOut(fileOut); err != nil {
return utils.NewErrServerError(err)
}
case utils.CSV:
csvWriter := csv.NewWriter(fileOut)
if err := cdre.writeCsv(csvWriter); err != nil {
return utils.NewErrServerError(err)
}
}
return nil
}
// Return the first exported Cdr OrderId
func (cdre *CdrExporter) FirstOrderId() int64 {
return cdre.firstExpOrderId
}
// Return the last exported Cdr OrderId
func (cdre *CdrExporter) LastOrderId() int64 {
return cdre.lastExpOrderId
}
// Return total cost in the exported cdrs
func (cdre *CdrExporter) TotalCost() float64 {
return cdre.totalCost
}
func (cdre *CdrExporter) TotalExportedCdrs() int {
return cdre.numberOfRecords
}
// Return successfully exported CgrIds
func (cdre *CdrExporter) PositiveExports() []string {
return cdre.positiveExports
}
// Return failed exported CgrIds together with the reason
func (cdre *CdrExporter) NegativeExports() map[string]string {
return cdre.negativeExports
}

124
cdre/cdrexporter_test.go Normal file
View File

@@ -0,0 +1,124 @@
/*
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 cdre
import (
"testing"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
func TestCdreGetCombimedCdrFieldVal(t *testing.T) {
cfg, _ := config.NewDefaultCGRConfig()
cdrs := []*engine.StoredCdr{
&engine.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Unix(1383813745, 0).UTC().String()), TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1",
ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813745, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(),
Usage: time.Duration(10) * time.Second, MediationRunId: "RUN_RTL", Cost: 1.01},
&engine.StoredCdr{CgrId: utils.Sha1("dsafdsaf2", time.Unix(1383813745, 0).UTC().String()), TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1",
ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813745, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(),
Usage: time.Duration(10) * time.Second, MediationRunId: "CUSTOMER1", Cost: 2.01},
&engine.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Unix(1383813745, 0).UTC().String()), TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1",
ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813745, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(),
Usage: time.Duration(10) * time.Second, MediationRunId: "CUSTOMER1", Cost: 3.01},
&engine.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Unix(1383813745, 0).UTC().String()), TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1",
ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813745, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(),
Usage: time.Duration(10) * time.Second, MediationRunId: utils.DEFAULT_RUNID, Cost: 4.01},
&engine.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Unix(1383813745, 0).UTC().String()), TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1",
ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1000", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813745, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(),
Usage: time.Duration(10) * time.Second, MediationRunId: "RETAIL1", Cost: 5.01},
}
cdre, err := NewCdrExporter(cdrs, nil, cfg.CdreProfiles["*default"], cfg.CdreProfiles["*default"].CdrFormat, cfg.CdreProfiles["*default"].FieldSeparator,
"firstexport", 0.0, 0.0, 0.0, 0.0, 0, 4, cfg.RoundingDecimals, "", 0, cfg.HttpSkipTlsVerify, "")
if err != nil {
t.Error("Unexpected error received: ", err)
}
fltrRule, _ := utils.ParseRSRFields("~mediation_runid:s/default/RUN_RTL/", utils.INFIELD_SEP)
val, _ := utils.ParseRSRFields("cost", utils.INFIELD_SEP)
cfgCdrFld := &config.CfgCdrField{Tag: "cost", Type: "cdrfield", CdrFieldId: "cost", Value: val, FieldFilter: fltrRule}
if costVal, err := cdre.getCombimedCdrFieldVal(cdrs[3], cfgCdrFld); err != nil {
t.Error(err)
} else if costVal != "1.01" {
t.Error("Expecting: 1.01, received: ", costVal)
}
fltrRule, _ = utils.ParseRSRFields("~mediation_runid:s/default/RETAIL1/", utils.INFIELD_SEP)
val, _ = utils.ParseRSRFields("account", utils.INFIELD_SEP)
cfgCdrFld = &config.CfgCdrField{Tag: "account", Type: "cdrfield", CdrFieldId: "account", Value: val, FieldFilter: fltrRule}
if acntVal, err := cdre.getCombimedCdrFieldVal(cdrs[3], cfgCdrFld); err != nil {
t.Error(err)
} else if acntVal != "1000" {
t.Error("Expecting: 1000, received: ", acntVal)
}
}
func TestGetDateTimeFieldVal(t *testing.T) {
cdreTst := new(CdrExporter)
cdrTst := &engine.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Unix(1383813745, 0).UTC().String()), TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1",
ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813745, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(),
Usage: time.Duration(10) * time.Second, MediationRunId: utils.DEFAULT_RUNID, Cost: 1.01,
ExtraFields: map[string]string{"stop_time": "2014-06-11 19:19:00 +0000 UTC", "fieldextr2": "valextr2"}}
val, _ := utils.ParseRSRFields("stop_time", utils.INFIELD_SEP)
layout := "2006-01-02 15:04:05"
cfgCdrFld := &config.CfgCdrField{Tag: "stop_time", Type: "cdrfield", CdrFieldId: "stop_time", Value: val, Layout: layout}
if cdrVal, err := cdreTst.getDateTimeFieldVal(cdrTst, cfgCdrFld); err != nil {
t.Error(err)
} else if cdrVal != "2014-06-11 19:19:00" {
t.Error("Expecting: 2014-06-11 19:19:00, got: ", cdrVal)
}
// Test filter
fltr, _ := utils.ParseRSRFields("~tenant:s/(.+)/itsyscom.com/", utils.INFIELD_SEP)
cfgCdrFld = &config.CfgCdrField{Tag: "stop_time", Type: "cdrfield", CdrFieldId: "stop_time", Value: val, FieldFilter: fltr, Layout: layout}
if _, err := cdreTst.getDateTimeFieldVal(cdrTst, cfgCdrFld); err == nil {
t.Error(err)
}
val, _ = utils.ParseRSRFields("fieldextr2", utils.INFIELD_SEP)
cfgCdrFld = &config.CfgCdrField{Tag: "stop_time", Type: "cdrfield", CdrFieldId: "stop_time", Value: val, Layout: layout}
// Test time parse error
if _, err := cdreTst.getDateTimeFieldVal(cdrTst, cfgCdrFld); err == nil {
t.Error("Should give error here, got none.")
}
}
func TestCdreCdrFieldValue(t *testing.T) {
cdre := new(CdrExporter)
cdr := &engine.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Unix(1383813745, 0).UTC().String()), TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1",
ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813745, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(),
Usage: time.Duration(10) * time.Second, MediationRunId: utils.DEFAULT_RUNID, Cost: 1.01}
val, _ := utils.ParseRSRFields("destination", utils.INFIELD_SEP)
cfgCdrFld := &config.CfgCdrField{Tag: "destination", Type: "cdrfield", CdrFieldId: "destination", Value: val}
if val, err := cdre.cdrFieldValue(cdr, cfgCdrFld); err != nil {
t.Error(err)
} else if val != cdr.Destination {
t.Errorf("Expecting: %s, received: %s", cdr.Destination, val)
}
fltr, _ := utils.ParseRSRFields("~tenant:s/(.+)/itsyscom.com/", utils.INFIELD_SEP)
cfgCdrFld = &config.CfgCdrField{Tag: "destination", Type: "cdrfield", CdrFieldId: "destination", Value: val, FieldFilter: fltr}
if _, err := cdre.cdrFieldValue(cdr, cfgCdrFld); err == nil {
t.Error("Failed to use filter")
}
}

87
cdre/csv_test.go Normal file
View File

@@ -0,0 +1,87 @@
/*
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 cdre
import (
"bytes"
"encoding/csv"
"strings"
"testing"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
func TestCsvCdrWriter(t *testing.T) {
writer := &bytes.Buffer{}
cfg, _ := config.NewDefaultCGRConfig()
storedCdr1 := &engine.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Unix(1383813745, 0).UTC().String()), TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1",
ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813745, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(),
Usage: time.Duration(10) * time.Second, MediationRunId: utils.DEFAULT_RUNID,
ExtraFields: map[string]string{"extra1": "val_extra1", "extra2": "val_extra2", "extra3": "val_extra3"}, Cost: 1.01,
}
cdre, err := NewCdrExporter([]*engine.StoredCdr{storedCdr1}, nil, cfg.CdreProfiles["*default"], utils.CSV, ',', "firstexport", 0.0, 0.0, 0.0, 0.0, 0, 4,
cfg.RoundingDecimals, "", 0, cfg.HttpSkipTlsVerify, "")
if err != nil {
t.Error("Unexpected error received: ", err)
}
csvWriter := csv.NewWriter(writer)
if err := cdre.writeCsv(csvWriter); err != nil {
t.Error("Unexpected error: ", err)
}
expected := `dbafe9c8614c785a65aabd116dd3959c3c56f7f6,*default,*voice,dsafdsaf,*rated,*out,cgrates.org,call,1001,1001,1002,2013-11-07T08:42:25Z,2013-11-07T08:42:26Z,10,1.0100`
result := strings.TrimSpace(writer.String())
if result != expected {
t.Errorf("Expected: \n%s received: \n%s.", expected, result)
}
if cdre.TotalCost() != 1.01 {
t.Error("Unexpected TotalCost: ", cdre.TotalCost())
}
}
func TestAlternativeFieldSeparator(t *testing.T) {
writer := &bytes.Buffer{}
cfg, _ := config.NewDefaultCGRConfig()
storedCdr1 := &engine.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Unix(1383813745, 0).UTC().String()), TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1",
ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813745, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(),
Usage: time.Duration(10) * time.Second, MediationRunId: utils.DEFAULT_RUNID,
ExtraFields: map[string]string{"extra1": "val_extra1", "extra2": "val_extra2", "extra3": "val_extra3"}, Cost: 1.01,
}
cdre, err := NewCdrExporter([]*engine.StoredCdr{storedCdr1}, nil, cfg.CdreProfiles["*default"], utils.CSV, '|',
"firstexport", 0.0, 0.0, 0.0, 0.0, 0, 4, cfg.RoundingDecimals, "", 0, cfg.HttpSkipTlsVerify, "")
if err != nil {
t.Error("Unexpected error received: ", err)
}
csvWriter := csv.NewWriter(writer)
if err := cdre.writeCsv(csvWriter); err != nil {
t.Error("Unexpected error: ", err)
}
expected := `dbafe9c8614c785a65aabd116dd3959c3c56f7f6|*default|*voice|dsafdsaf|*rated|*out|cgrates.org|call|1001|1001|1002|2013-11-07T08:42:25Z|2013-11-07T08:42:26Z|10|1.0100`
result := strings.TrimSpace(writer.String())
if result != expected {
t.Errorf("Expected: \n%s received: \n%s.", expected, result)
}
if cdre.TotalCost() != 1.01 {
t.Error("Unexpected TotalCost: ", cdre.TotalCost())
}
}

241
cdre/fixedwidth_test.go Normal file
View File

@@ -0,0 +1,241 @@
/*
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 cdre
import (
"bytes"
"math"
"testing"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
var hdrJsnCfgFlds = []*config.CdrFieldJsonCfg{
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("TypeOfRecord"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("10"), Width: utils.IntPointer(2)},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("Filler1"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(3)},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("DistributorCode"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("VOI"), Width: utils.IntPointer(3)},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("FileSeqNr"), Type: utils.StringPointer(utils.METATAG), Value: utils.StringPointer(META_EXPORTID),
Width: utils.IntPointer(5), Strip: utils.StringPointer("right"), Padding: utils.StringPointer("zeroleft")},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("LastCdr"), Type: utils.StringPointer(utils.METATAG), Value: utils.StringPointer(META_LASTCDRATIME),
Width: utils.IntPointer(12), Layout: utils.StringPointer("020106150400")},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("FileCreationfTime"), Type: utils.StringPointer(utils.METATAG), Value: utils.StringPointer(META_TIMENOW),
Width: utils.IntPointer(12), Layout: utils.StringPointer("020106150400")},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("FileVersion"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("01"), Width: utils.IntPointer(2)},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("Filler2"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(105)},
}
var contentJsnCfgFlds = []*config.CdrFieldJsonCfg{
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("TypeOfRecord"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("20"), Width: utils.IntPointer(2)},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("Account"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.ACCOUNT), Width: utils.IntPointer(12),
Strip: utils.StringPointer("left"), Padding: utils.StringPointer("right")},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("Subject"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.SUBJECT), Width: utils.IntPointer(5),
Strip: utils.StringPointer("right"), Padding: utils.StringPointer("right")},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("CLI"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer("cli"), Width: utils.IntPointer(15),
Strip: utils.StringPointer("xright"), Padding: utils.StringPointer("right")},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("Destination"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.DESTINATION), Width: utils.IntPointer(24),
Strip: utils.StringPointer("xright"), Padding: utils.StringPointer("right")},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("TOR"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("02"), Width: utils.IntPointer(2)},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("SubtypeTOR"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("11"), Width: utils.IntPointer(4),
Padding: utils.StringPointer("right")},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("SetupTime"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.SETUP_TIME), Width: utils.IntPointer(12),
Strip: utils.StringPointer("right"), Padding: utils.StringPointer("right"), Layout: utils.StringPointer("020106150400")},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("Duration"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.USAGE), Width: utils.IntPointer(6),
Strip: utils.StringPointer("right"), Padding: utils.StringPointer("right"), Layout: utils.StringPointer(utils.SECONDS)},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("DataVolume"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(6)},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("TaxCode"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("1"), Width: utils.IntPointer(1)},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("OperatorCode"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer("opercode"), Width: utils.IntPointer(2),
Strip: utils.StringPointer("right"), Padding: utils.StringPointer("right")},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("ProductId"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer("productid"), Width: utils.IntPointer(5),
Strip: utils.StringPointer("right"), Padding: utils.StringPointer("right")},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("NetworkId"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("3"), Width: utils.IntPointer(1)},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("CallId"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.ACCID), Width: utils.IntPointer(16),
Padding: utils.StringPointer("right")},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("Filler"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(8)},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("Filler"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(8)},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("TerminationCode"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer("operator;product"),
Width: utils.IntPointer(5), Strip: utils.StringPointer("right"), Padding: utils.StringPointer("right")},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("Cost"), Type: utils.StringPointer(utils.CDRFIELD), Value: utils.StringPointer(utils.COST), Width: utils.IntPointer(9),
Padding: utils.StringPointer("zeroleft")},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("DestinationPrivacy"), Type: utils.StringPointer(utils.METATAG), Value: utils.StringPointer(META_MASKDESTINATION),
Width: utils.IntPointer(1)},
}
var trailerJsnCfgFlds = []*config.CdrFieldJsonCfg{
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("TypeOfRecord"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("90"), Width: utils.IntPointer(2)},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("Filler1"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(3)},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("DistributorCode"), Type: utils.StringPointer(utils.CONSTANT), Value: utils.StringPointer("VOI"), Width: utils.IntPointer(3)},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("FileSeqNr"), Type: utils.StringPointer(utils.METATAG), Value: utils.StringPointer(META_EXPORTID), Width: utils.IntPointer(5),
Strip: utils.StringPointer("right"), Padding: utils.StringPointer("zeroleft")},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("NumberOfRecords"), Type: utils.StringPointer(utils.METATAG), Value: utils.StringPointer(META_NRCDRS),
Width: utils.IntPointer(6), Padding: utils.StringPointer("zeroleft")},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("CdrsDuration"), Type: utils.StringPointer(utils.METATAG), Value: utils.StringPointer(META_DURCDRS),
Width: utils.IntPointer(8), Padding: utils.StringPointer("zeroleft"), Layout: utils.StringPointer(utils.SECONDS)},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("FirstCdrTime"), Type: utils.StringPointer(utils.METATAG), Value: utils.StringPointer(META_FIRSTCDRATIME),
Width: utils.IntPointer(12), Layout: utils.StringPointer("020106150400")},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("LastCdrTime"), Type: utils.StringPointer(utils.METATAG), Value: utils.StringPointer(META_LASTCDRATIME),
Width: utils.IntPointer(12), Layout: utils.StringPointer("020106150400")},
&config.CdrFieldJsonCfg{Tag: utils.StringPointer("Filler2"), Type: utils.StringPointer(utils.FILLER), Width: utils.IntPointer(93)},
}
var hdrCfgFlds, contentCfgFlds, trailerCfgFlds []*config.CfgCdrField
// Write one CDR and test it's results only for content buffer
func TestWriteCdr(t *testing.T) {
var err error
wrBuf := &bytes.Buffer{}
cfg, _ := config.NewDefaultCGRConfig()
if hdrCfgFlds, err = config.CfgCdrFieldsFromCdrFieldsJsonCfg(hdrJsnCfgFlds); err != nil {
t.Error(err)
}
if contentCfgFlds, err = config.CfgCdrFieldsFromCdrFieldsJsonCfg(contentJsnCfgFlds); err != nil {
t.Error(err)
}
if trailerCfgFlds, err = config.CfgCdrFieldsFromCdrFieldsJsonCfg(trailerJsnCfgFlds); err != nil {
t.Error(err)
}
cdreCfg := &config.CdreConfig{
CdrFormat: utils.CDRE_FIXED_WIDTH,
HeaderFields: hdrCfgFlds,
ContentFields: contentCfgFlds,
TrailerFields: trailerCfgFlds,
}
cdr := &engine.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()),
TOR: utils.VOICE, OrderId: 1, AccId: "dsafdsaf", CdrHost: "192.168.1.1",
ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC),
AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC),
Usage: time.Duration(10) * time.Second, MediationRunId: utils.DEFAULT_RUNID, Cost: 2.34567,
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
}
cdre, err := NewCdrExporter([]*engine.StoredCdr{cdr}, nil, cdreCfg, utils.CDRE_FIXED_WIDTH, ',', "fwv_1", 0.0, 0.0, 0.0, 0.0, 0, 4,
cfg.RoundingDecimals, "", -1, cfg.HttpSkipTlsVerify, "")
if err != nil {
t.Error(err)
}
eHeader := "10 VOIfwv_107111308420018011511340001 \n"
eContentOut := "201001 1001 1002 0211 07111308420010 1 3dsafdsaf 0002.34570\n"
eTrailer := "90 VOIfwv_100000100000010071113084200071113084200 \n"
if err := cdre.writeOut(wrBuf); err != nil {
t.Error(err)
}
allOut := wrBuf.String()
eAllOut := eHeader + eContentOut + eTrailer
if math.Mod(float64(len(allOut)), 145) != 0 {
t.Error("Unexpected export content length", len(allOut))
} else if len(allOut) != len(eAllOut) {
t.Errorf("Output does not match expected length. Have output %q, expecting: %q", allOut, eAllOut)
}
// Test stats
if !cdre.firstCdrATime.Equal(cdr.AnswerTime) {
t.Error("Unexpected firstCdrATime in stats: ", cdre.firstCdrATime)
} else if !cdre.lastCdrATime.Equal(cdr.AnswerTime) {
t.Error("Unexpected lastCdrATime in stats: ", cdre.lastCdrATime)
} else if cdre.numberOfRecords != 1 {
t.Error("Unexpected number of records in the stats: ", cdre.numberOfRecords)
} else if cdre.totalDuration != cdr.Usage {
t.Error("Unexpected total duration in the stats: ", cdre.totalDuration)
} else if cdre.totalCost != utils.Round(cdr.Cost, cdre.roundDecimals, utils.ROUNDING_MIDDLE) {
t.Error("Unexpected total cost in the stats: ", cdre.totalCost)
}
if cdre.FirstOrderId() != 1 {
t.Error("Unexpected FirstOrderId", cdre.FirstOrderId())
}
if cdre.LastOrderId() != 1 {
t.Error("Unexpected LastOrderId", cdre.LastOrderId())
}
if cdre.TotalCost() != utils.Round(cdr.Cost, cdre.roundDecimals, utils.ROUNDING_MIDDLE) {
t.Error("Unexpected TotalCost: ", cdre.TotalCost())
}
}
func TestWriteCdrs(t *testing.T) {
wrBuf := &bytes.Buffer{}
cdreCfg := &config.CdreConfig{
CdrFormat: utils.CDRE_FIXED_WIDTH,
HeaderFields: hdrCfgFlds,
ContentFields: contentCfgFlds,
TrailerFields: trailerCfgFlds,
}
cdr1 := &engine.StoredCdr{CgrId: utils.Sha1("aaa1", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()),
TOR: utils.VOICE, OrderId: 2, AccId: "aaa1", CdrHost: "192.168.1.1", ReqType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1001", Subject: "1001", Destination: "1010",
SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC),
AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC),
Usage: time.Duration(10) * time.Second, MediationRunId: utils.DEFAULT_RUNID, Cost: 2.25,
ExtraFields: map[string]string{"productnumber": "12341", "fieldextr2": "valextr2"},
}
cdr2 := &engine.StoredCdr{CgrId: utils.Sha1("aaa2", time.Date(2013, 11, 7, 7, 42, 20, 0, time.UTC).String()),
TOR: utils.VOICE, OrderId: 4, AccId: "aaa2", CdrHost: "192.168.1.2", ReqType: utils.META_PREPAID, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1002", Subject: "1002", Destination: "1011",
SetupTime: time.Date(2013, 11, 7, 7, 42, 20, 0, time.UTC),
AnswerTime: time.Date(2013, 11, 7, 7, 42, 26, 0, time.UTC),
Usage: time.Duration(5) * time.Minute, MediationRunId: utils.DEFAULT_RUNID, Cost: 1.40001,
ExtraFields: map[string]string{"productnumber": "12342", "fieldextr2": "valextr2"},
}
cdr3 := &engine.StoredCdr{}
cdr4 := &engine.StoredCdr{CgrId: utils.Sha1("aaa3", time.Date(2013, 11, 7, 9, 42, 18, 0, time.UTC).String()),
TOR: utils.VOICE, OrderId: 3, AccId: "aaa4", CdrHost: "192.168.1.4", ReqType: utils.META_POSTPAID, Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1004", Subject: "1004", Destination: "1013",
SetupTime: time.Date(2013, 11, 7, 9, 42, 18, 0, time.UTC),
AnswerTime: time.Date(2013, 11, 7, 9, 42, 26, 0, time.UTC),
Usage: time.Duration(20) * time.Second, MediationRunId: utils.DEFAULT_RUNID, Cost: 2.34567,
ExtraFields: map[string]string{"productnumber": "12344", "fieldextr2": "valextr2"},
}
cfg, _ := config.NewDefaultCGRConfig()
cdre, err := NewCdrExporter([]*engine.StoredCdr{cdr1, cdr2, cdr3, cdr4}, nil, cdreCfg, utils.CDRE_FIXED_WIDTH, ',',
"fwv_1", 0.0, 0.0, 0.0, 0.0, 0, 4, cfg.RoundingDecimals, "", -1, cfg.HttpSkipTlsVerify, "")
if err != nil {
t.Error(err)
}
if err := cdre.writeOut(wrBuf); err != nil {
t.Error(err)
}
if len(wrBuf.String()) != 725 {
t.Error("Output buffer does not contain expected info. Expecting len: 725, got: ", len(wrBuf.String()))
}
// Test stats
if !cdre.firstCdrATime.Equal(cdr2.AnswerTime) {
t.Error("Unexpected firstCdrATime in stats: ", cdre.firstCdrATime)
}
if !cdre.lastCdrATime.Equal(cdr4.AnswerTime) {
t.Error("Unexpected lastCdrATime in stats: ", cdre.lastCdrATime)
}
if cdre.numberOfRecords != 3 {
t.Error("Unexpected number of records in the stats: ", cdre.numberOfRecords)
}
if cdre.totalDuration != time.Duration(330)*time.Second {
t.Error("Unexpected total duration in the stats: ", cdre.totalDuration)
}
if cdre.totalCost != 5.9957 {
t.Error("Unexpected total cost in the stats: ", cdre.totalCost)
}
if cdre.FirstOrderId() != 2 {
t.Error("Unexpected FirstOrderId", cdre.FirstOrderId())
}
if cdre.LastOrderId() != 4 {
t.Error("Unexpected LastOrderId", cdre.LastOrderId())
}
if cdre.TotalCost() != 5.9957 {
t.Error("Unexpected TotalCost: ", cdre.TotalCost())
}
}

93
cdre/libcdre.go Normal file
View File

@@ -0,0 +1,93 @@
/*
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 cdre
import (
"errors"
"fmt"
"github.com/cgrates/cgrates/utils"
)
// Used as generic function logic for various fields
// Attributes
// source - the base source
// width - the field width
// strip - if present it will specify the strip strategy, when missing strip will not be allowed
// padding - if present it will specify the padding strategy to use, left, right, zeroleft, zeroright
func FmtFieldWidth(source string, width int, strip, padding string, mandatory bool) (string, error) {
if mandatory && len(source) == 0 {
return "", errors.New("Empty source value")
}
if width == 0 { // Disable width processing if not defined
return source, nil
}
if len(source) == width { // the source is exactly the maximum length
return source, nil
}
if len(source) > width { //the source is bigger than allowed
if len(strip) == 0 {
return "", fmt.Errorf("Source %s is bigger than the width %d, no strip defied", source, width)
}
if strip == "right" {
return source[:width], nil
} else if strip == "xright" {
return source[:width-1] + "x", nil // Suffix with x to mark prefix
} else if strip == "left" {
diffIndx := len(source) - width
return source[diffIndx:], nil
} else if strip == "xleft" { // Prefix one x to mark stripping
diffIndx := len(source) - width
return "x" + source[diffIndx+1:], nil
}
} else { //the source is smaller as the maximum allowed
if len(padding) == 0 {
return "", fmt.Errorf("Source %s is smaller than the width %d, no padding defined", source, width)
}
var paddingFmt string
switch padding {
case "right":
paddingFmt = fmt.Sprintf("%%-%ds", width)
case "left":
paddingFmt = fmt.Sprintf("%%%ds", width)
case "zeroleft":
paddingFmt = fmt.Sprintf("%%0%ds", width)
}
if len(paddingFmt) != 0 {
return fmt.Sprintf(paddingFmt, source), nil
}
}
return source, nil
}
// Mask a number of characters in the suffix of the destination
func MaskDestination(dest string, maskLen int) string {
destLen := len(dest)
if maskLen < 0 {
return dest
} else if maskLen > destLen {
maskLen = destLen
}
dest = dest[:destLen-maskLen]
for i := 0; i < maskLen; i++ {
dest += utils.MASK_CHAR
}
return dest
}

133
cdre/libcdre_test.go Normal file
View File

@@ -0,0 +1,133 @@
/*
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 cdre
import (
"testing"
)
func TestMandatory(t *testing.T) {
_, err := FmtFieldWidth("", 0, "", "", true)
if err == nil {
t.Errorf("Failed to detect mandatory value")
}
}
func TestMaxLen(t *testing.T) {
result, err := FmtFieldWidth("test", 4, "", "", false)
expected := "test"
if err != nil || result != expected {
t.Errorf("Expected \"test\" was \"%s\"", result)
}
}
func TestRPadding(t *testing.T) {
result, err := FmtFieldWidth("test", 8, "", "right", false)
expected := "test "
if err != nil || result != expected {
t.Errorf("Expected \"%s \" was \"%s\"", expected, result)
}
}
func TestPaddingFiller(t *testing.T) {
result, err := FmtFieldWidth("", 8, "", "right", false)
expected := " "
if err != nil || result != expected {
t.Errorf("Expected \"%s \" was \"%s\"", expected, result)
}
}
func TestLPadding(t *testing.T) {
result, err := FmtFieldWidth("test", 8, "", "left", false)
expected := " test"
if err != nil || result != expected {
t.Errorf("Expected \"%s \" was \"%s\"", expected, result)
}
}
func TestZeroLPadding(t *testing.T) {
result, err := FmtFieldWidth("test", 8, "", "zeroleft", false)
expected := "0000test"
if err != nil || result != expected {
t.Errorf("Expected \"%s \" was \"%s\"", expected, result)
}
}
func TestRStrip(t *testing.T) {
result, err := FmtFieldWidth("test", 2, "right", "", false)
expected := "te"
if err != nil || result != expected {
t.Errorf("Expected \"%s \" was \"%s\"", expected, result)
}
}
func TestXRStrip(t *testing.T) {
result, err := FmtFieldWidth("test", 3, "xright", "", false)
expected := "tex"
if err != nil || result != expected {
t.Errorf("Expected \"%s \" was \"%s\"", expected, result)
}
}
func TestLStrip(t *testing.T) {
result, err := FmtFieldWidth("test", 2, "left", "", false)
expected := "st"
if err != nil || result != expected {
t.Errorf("Expected \"%s \" was \"%s\"", expected, result)
}
}
func TestXLStrip(t *testing.T) {
result, err := FmtFieldWidth("test", 3, "xleft", "", false)
expected := "xst"
if err != nil || result != expected {
t.Errorf("Expected \"%s \" was \"%s\"", expected, result)
}
}
func TestStripNotAllowed(t *testing.T) {
_, err := FmtFieldWidth("test", 3, "", "", false)
if err == nil {
t.Error("Expected error")
}
}
func TestPaddingNotAllowed(t *testing.T) {
_, err := FmtFieldWidth("test", 5, "", "", false)
if err == nil {
t.Error("Expected error")
}
}
func TestMaskDestination(t *testing.T) {
dest := "+4986517174963"
if destMasked := MaskDestination(dest, 3); destMasked != "+4986517174***" {
t.Error("Unexpected mask applied", destMasked)
}
if destMasked := MaskDestination(dest, -1); destMasked != dest {
t.Error("Negative maskLen should not modify destination", destMasked)
}
if destMasked := MaskDestination(dest, 0); destMasked != dest {
t.Error("Zero maskLen should not modify destination", destMasked)
}
if destMasked := MaskDestination(dest, 100); destMasked != "**************" {
t.Error("High maskLen should return complete mask", destMasked)
}
}

View File

@@ -1,6 +1,6 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2013 ITsysCOM
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
@@ -19,49 +19,164 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package main
import (
"encoding/json"
"flag"
"fmt"
"github.com/cgrates/cgrates/console"
"github.com/cgrates/cgrates/rater"
"io"
"log"
"net/rpc"
"net/rpc/jsonrpc"
"os"
"strings"
"github.com/cgrates/cgrates/console"
"github.com/cgrates/cgrates/utils"
"github.com/cgrates/rpcclient"
"github.com/peterh/liner"
)
var (
history_fn = os.Getenv("HOME") + "/.cgr_history"
version = flag.Bool("version", false, "Prints the application version.")
verbose = flag.Bool("verbose", false, "Show extra info about command execution.")
server = flag.String("server", "127.0.0.1:2012", "server address host:port")
rpc_encoding = flag.String("rpc_encoding", "gob", "RPC encoding used <gob|json>")
rpc_encoding = flag.String("rpc_encoding", "json", "RPC encoding used <gob|json>")
client *rpcclient.RpcClient
)
func executeCommand(command string) {
if strings.TrimSpace(command) == "" {
return
}
if strings.TrimSpace(command) == "help" {
commands := console.GetCommands()
fmt.Println("Commands:")
for name, cmd := range commands {
fmt.Print(name, cmd.Usage())
}
return
}
if strings.HasPrefix(command, "help") {
words := strings.Split(command, " ")
if len(words) > 1 {
commands := console.GetCommands()
if cmd, ok := commands[words[1]]; ok {
fmt.Print(cmd.Usage())
} else {
fmt.Print("Available commands: ")
for name, _ := range commands {
fmt.Print(name + " ")
}
fmt.Println()
}
return
}
}
cmd, cmdErr := console.GetCommandValue(command, *verbose)
if cmdErr != nil {
fmt.Println(cmdErr)
return
}
if cmd.RpcMethod() != "" {
res := cmd.RpcResult()
param := cmd.RpcParams(false)
//log.Print(reflect.TypeOf(param))
switch param.(type) {
case *console.EmptyWrapper:
param = ""
case *console.StringWrapper:
param = param.(*console.StringWrapper).Item
case *console.StringSliceWrapper:
param = param.(*console.StringSliceWrapper).Items
case *console.StringMapWrapper:
param = param.(*console.StringMapWrapper).Items
}
//log.Printf("Param: %+v", param)
if rpcErr := client.Call(cmd.RpcMethod(), param, res); rpcErr != nil {
fmt.Println("Error executing command: " + rpcErr.Error())
} else {
result, _ := json.MarshalIndent(res, "", " ")
fmt.Println(string(result))
}
} else {
fmt.Println(cmd.LocalExecute())
}
}
func main() {
flag.Parse()
if *version {
fmt.Println("CGRateS " + rater.VERSION)
fmt.Println("CGRateS " + utils.VERSION)
return
}
var client *rpc.Client
var err error
if *rpc_encoding == "json" {
client, err = jsonrpc.Dial("tcp", *server)
} else {
client, err = rpc.Dial("tcp", *server)
}
client, err = rpcclient.NewRpcClient("tcp", *server, 3, 3, *rpc_encoding)
if err != nil {
flag.PrintDefaults()
log.Fatal("Could not connect to server " + *server)
}
defer client.Close()
// Strict command parsing starts here
args := append([]string{os.Args[0]}, flag.Args()...) // Emulate os.Args by prepending the cmd to list of args coming from flag
cmd, cmdErr := console.GetCommandValue(args)
if cmdErr != nil {
log.Fatal(cmdErr)
}
res := cmd.RpcResult()
if rpcErr := client.Call(cmd.RpcMethod(), cmd.RpcParams(), res); rpcErr != nil {
}
fmt.Println("Result:", res)
if len(flag.Args()) != 0 {
executeCommand(strings.Join(flag.Args(), " "))
return
}
fmt.Println("Welcome to CGRateS console!")
fmt.Print("Type `help` for a list of commands\n\n")
line := liner.NewLiner()
defer line.Close()
line.SetCompleter(func(line string) (comp []string) {
commands := console.GetCommands()
for name, cmd := range commands {
if strings.HasPrefix(name, strings.ToLower(line)) {
comp = append(comp, name)
}
// try arguments
if strings.HasPrefix(line, name) {
// get last word
lastSpace := strings.LastIndex(line, " ")
lastSpace += 1
for _, arg := range cmd.ClientArgs() {
if strings.HasPrefix(arg, line[lastSpace:]) {
comp = append(comp, line[:lastSpace]+arg)
}
}
}
}
return
})
if f, err := os.Open(history_fn); err == nil {
line.ReadHistory(f)
f.Close()
}
stop := false
for !stop {
if command, err := line.Prompt("cgr> "); err != nil {
if err == io.EOF {
fmt.Println("\nbye!")
stop = true
} else {
fmt.Print("Error reading line: ", err)
}
} else {
line.AppendHistory(command)
switch strings.ToLower(strings.TrimSpace(command)) {
case "quit", "exit", "bye", "close":
fmt.Println("\nbye!")
stop = true
default:
executeCommand(command)
}
}
}
if f, err := os.Create(history_fn); err != nil {
log.Print("Error writing history file: ", err)
} else {
line.WriteHistory(f)
f.Close()
}
}

View File

@@ -0,0 +1,669 @@
/*
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 main
import (
"flag"
"fmt"
"log"
"os"
"reflect"
"runtime"
"runtime/pprof"
"strconv"
"time"
"github.com/cgrates/cgrates/apier/v1"
"github.com/cgrates/cgrates/apier/v2"
"github.com/cgrates/cgrates/balancer2go"
"github.com/cgrates/cgrates/cdrc"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/history"
"github.com/cgrates/cgrates/scheduler"
"github.com/cgrates/cgrates/sessionmanager"
"github.com/cgrates/cgrates/utils"
"github.com/cgrates/rpcclient"
)
const (
JSON = "json"
GOB = "gob"
POSTGRES = "postgres"
MYSQL = "mysql"
MONGO = "mongo"
REDIS = "redis"
SAME = "same"
FS = "freeswitch"
KAMAILIO = "kamailio"
OSIPS = "opensips"
)
var (
cfgDir = flag.String("config_dir", utils.CONFIG_DIR, "Configuration directory path.")
version = flag.Bool("version", false, "Prints the application version.")
raterEnabled = flag.Bool("rater", false, "Enforce starting of the rater daemon overwriting config")
schedEnabled = flag.Bool("scheduler", false, "Enforce starting of the scheduler daemon .overwriting config")
cdrsEnabled = flag.Bool("cdrs", false, "Enforce starting of the cdrs daemon overwriting config")
pidFile = flag.String("pid", "", "Write pid file")
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
singlecpu = flag.Bool("singlecpu", false, "Run on single CPU core")
cfg *config.CGRConfig
sms []sessionmanager.SessionManager
smRpc *v1.SessionManagerV1
err error
)
func startCdrcs(internalCdrSChan chan *engine.CdrServer, internalRaterChan chan *engine.Responder, exitChan chan bool) {
for {
cdrcChildrenChan := make(chan struct{}) // Will use it to communicate with the children of one fork
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
}
if cdrcCfg.Enabled == false {
continue // Ignore not enabled
}
go startCdrc(internalCdrSChan, internalRaterChan, cdrcCfgs, cfg.HttpSkipTlsVerify, cdrcChildrenChan, exitChan)
}
select {
case <-exitChan: // Stop forking CDRCs
break
case <-cfg.ConfigReloads[utils.CDRC]: // Consume the load request and wait for a new one
close(cdrcChildrenChan) // Stop all the children of the previous run
engine.Logger.Info("<CDRC> Configuration reload")
continue
}
}
}
// Fires up a cdrc instance
func startCdrc(internalCdrSChan chan *engine.CdrServer, internalRaterChan chan *engine.Responder, cdrcCfgs map[string]*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)
if err != nil {
engine.Logger.Crit(fmt.Sprintf("<CDRC> Could not connect to CDRS via RPC: %v", err))
exitChan <- true
return
}
cdrsConn = &engine.RPCClientConnector{Client: conn}
}
cdrc, err := cdrc.NewCdrc(cdrcCfgs, httpSkipTlsCheck, cdrsConn, closeChan, cfg.DefaultTimezone)
if err != nil {
engine.Logger.Crit(fmt.Sprintf("Cdrc config parsing error: %s", err.Error()))
exitChan <- true
return
}
if err := cdrc.Run(); err != nil {
engine.Logger.Crit(fmt.Sprintf("Cdrc run error: %s", err.Error()))
exitChan <- true // If run stopped, something is bad, stop the application
}
}
func startSmFreeSWITCH(internalRaterChan chan *engine.Responder, cdrDb engine.CdrStorage, exitChan chan bool) {
engine.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)
if err != nil { //Connected so no need to reiterate
engine.Logger.Crit(fmt.Sprintf("<SM-FreeSWITCH> Could not connect to rater via RPC: %v", err))
exitChan <- true
return
}
raterConn = &engine.RPCClientConnector{Client: client}
}
}
// 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)
if err != nil {
engine.Logger.Crit(fmt.Sprintf("<SM-FreeSWITCH> Could not connect to CDRS via RPC: %v", err))
exitChan <- true
return
}
cdrsConn = &engine.RPCClientConnector{Client: client}
}
}
}
sm := sessionmanager.NewFSSessionManager(cfg.SmFsConfig, raterConn, cdrsConn, cfg.DefaultTimezone)
sms = append(sms, sm)
smRpc.SMs = append(smRpc.SMs, sm)
if err = sm.Connect(); err != nil {
engine.Logger.Err(fmt.Sprintf("<SessionManager> error: %s!", err))
}
exitChan <- true
}
func startSmKamailio(internalRaterChan chan *engine.Responder, cdrDb engine.CdrStorage, exitChan chan bool) {
engine.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)
if err != nil { //Connected so no need to reiterate
engine.Logger.Crit(fmt.Sprintf("<SM-FreeSWITCH> Could not connect to rater via RPC: %v", err))
exitChan <- true
return
}
raterConn = &engine.RPCClientConnector{Client: client}
}
}
// 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)
if err != nil {
engine.Logger.Crit(fmt.Sprintf("<SM-FreeSWITCH> Could not connect to CDRS via RPC: %v", err))
exitChan <- true
return
}
cdrsConn = &engine.RPCClientConnector{Client: client}
}
}
}
sm, _ := sessionmanager.NewKamailioSessionManager(cfg.SmKamConfig, raterConn, cdrsConn, cfg.DefaultTimezone)
sms = append(sms, sm)
smRpc.SMs = append(smRpc.SMs, sm)
if err = sm.Connect(); err != nil {
engine.Logger.Err(fmt.Sprintf("<SessionManager> error: %s!", err))
}
exitChan <- true
}
func startSmOpenSIPS(internalRaterChan chan *engine.Responder, cdrDb engine.CdrStorage, exitChan chan bool) {
engine.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)
if err != nil { //Connected so no need to reiterate
engine.Logger.Crit(fmt.Sprintf("<SM-FreeSWITCH> Could not connect to rater via RPC: %v", err))
exitChan <- true
return
}
raterConn = &engine.RPCClientConnector{Client: client}
}
}
// 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)
if err != nil {
engine.Logger.Crit(fmt.Sprintf("<SM-FreeSWITCH> Could not connect to CDRS via RPC: %v", err))
exitChan <- true
return
}
cdrsConn = &engine.RPCClientConnector{Client: client}
}
}
}
sm, _ := sessionmanager.NewOSipsSessionManager(cfg.SmOsipsConfig, cfg.Reconnects, raterConn, cdrsConn, cfg.DefaultTimezone)
sms = append(sms, sm)
smRpc.SMs = append(smRpc.SMs, sm)
if err := sm.Connect(); err != nil {
engine.Logger.Err(fmt.Sprintf("<SM-OpenSIPS> error: %s!", err))
}
exitChan <- true
}
func startCDRS(internalCdrSChan chan *engine.CdrServer, logDb engine.LogStorage, cdrDb engine.CdrStorage,
internalRaterChan chan *engine.Responder, internalPubSubSChan chan engine.PublisherSubscriber,
internalUserSChan chan engine.UserService, internalAliaseSChan chan engine.AliasService,
internalCdrStatSChan chan engine.StatsInterface, server *engine.Server, exitChan chan bool) {
engine.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)
if err != nil {
engine.Logger.Crit(fmt.Sprintf("<CDRS> Could not connect to rater: %s", err.Error()))
exitChan <- true
return
}
raterConn = &engine.RPCClientConnector{Client: client}
}
// Pubsub connection init
var pubSubConn engine.PublisherSubscriber
if cfg.CDRSPubSub == utils.INTERNAL {
pubSubs := <-internalPubSubSChan
pubSubConn = pubSubs
internalPubSubSChan <- pubSubs
} else if len(cfg.CDRSPubSub) != 0 {
if cfg.CDRSRater == cfg.CDRSPubSub {
pubSubConn = &engine.ProxyPubSub{Client: client}
} else {
client, err = rpcclient.NewRpcClient("tcp", cfg.CDRSPubSub, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB)
if err != nil {
engine.Logger.Crit(fmt.Sprintf("<CDRS> Could not connect to pubsub server: %s", err.Error()))
exitChan <- true
return
}
pubSubConn = &engine.ProxyPubSub{Client: 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)
if err != nil {
engine.Logger.Crit(fmt.Sprintf("<CDRS> Could not connect to users server: %s", err.Error()))
exitChan <- true
return
}
usersConn = &engine.ProxyUserService{Client: client}
}
}
// 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)
if err != nil {
engine.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)
if err != nil {
engine.Logger.Crit(fmt.Sprintf("<CDRS> Could not connect to stats server: %s", err.Error()))
exitChan <- true
return
}
statsConn = &engine.ProxyStats{Client: client}
}
}
cdrServer, _ := engine.NewCdrServer(cfg, cdrDb, raterConn, pubSubConn, usersConn, aliasesConn, statsConn)
engine.Logger.Info("Registering CDRS HTTP Handlers.")
cdrServer.RegisterHanlersToServer(server)
engine.Logger.Info("Registering CDRS RPC service.")
cdrSrv := v1.CdrsV1{CdrSrv: cdrServer}
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
}
func startScheduler(internalSchedulerChan chan *scheduler.Scheduler, ratingDb engine.RatingStorage, exitChan chan bool) {
engine.Logger.Info("Starting CGRateS Scheduler.")
sched := scheduler.NewScheduler()
go reloadSchedulerSingnalHandler(sched, ratingDb)
time.Sleep(1)
internalSchedulerChan <- sched
sched.LoadActionPlans(ratingDb)
sched.Loop()
exitChan <- true // Should not get out of loop though
}
func startCdrStats(internalCdrStatSChan chan engine.StatsInterface, ratingDb engine.RatingStorage, accountDb engine.AccountingStorage, server *engine.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 *engine.Server, exitChan chan bool) {
scribeServer, err := history.NewFileScribe(cfg.HistoryDir, cfg.HistorySaveInterval)
if err != nil {
engine.Logger.Crit(fmt.Sprintf("<HistoryServer> Could not start, error: %s", err.Error()))
exitChan <- true
}
server.RpcRegisterName("ScribeV1", scribeServer)
internalHistorySChan <- scribeServer
}
func startPubSubServer(internalPubSubSChan chan engine.PublisherSubscriber, accountDb engine.AccountingStorage, server *engine.Server) {
pubSubServer := engine.NewPubSub(accountDb, cfg.HttpSkipTlsVerify)
server.RpcRegisterName("PubSubV1", pubSubServer)
internalPubSubSChan <- pubSubServer
}
// ToDo: Make sure we are caching before starting this one
func startAliasesServer(internalAliaseSChan chan engine.AliasService, accountDb engine.AccountingStorage, server *engine.Server, exitChan chan bool) {
aliasesServer := engine.NewAliasHandler(accountDb)
server.RpcRegisterName("AliasesV1", aliasesServer)
if err := accountDb.CacheAccountingPrefixes(utils.ALIASES_PREFIX); err != nil {
engine.Logger.Crit(fmt.Sprintf("<Aliases> Could not start, error: %s", err.Error()))
exitChan <- true
return
}
internalAliaseSChan <- aliasesServer
}
func startUsersServer(internalUserSChan chan engine.UserService, accountDb engine.AccountingStorage, server *engine.Server, exitChan chan bool) {
userServer, err := engine.NewUserMap(accountDb, cfg.UserServerIndexes)
if err != nil {
engine.Logger.Crit(fmt.Sprintf("<UsersService> Could not start, error: %s", err.Error()))
exitChan <- true
return
}
server.RpcRegisterName("UsersV1", userServer)
internalUserSChan <- userServer
}
func startRpc(server *engine.Server, internalRaterChan chan *engine.Responder,
internalCdrSChan chan *engine.CdrServer,
internalCdrStatSChan chan engine.StatsInterface,
internalHistorySChan chan history.Scribe,
internalPubSubSChan chan engine.PublisherSubscriber,
internalUserSChan chan engine.UserService,
internalAliaseSChan chan engine.AliasService) {
select { // Any of the rpc methods will unlock listening to rpc requests
case resp := <-internalRaterChan:
internalRaterChan <- resp
case cdrs := <-internalCdrSChan:
internalCdrSChan <- cdrs
case cdrstats := <-internalCdrStatSChan:
internalCdrStatSChan <- cdrstats
case hist := <-internalHistorySChan:
internalHistorySChan <- hist
case pubsubs := <-internalPubSubSChan:
internalPubSubSChan <- pubsubs
case users := <-internalUserSChan:
internalUserSChan <- users
case aliases := <-internalAliaseSChan:
internalAliaseSChan <- aliases
}
go server.ServeJSON(cfg.RPCJSONListen)
go server.ServeGOB(cfg.RPCGOBListen)
go server.ServeHTTP(cfg.HTTPListen)
}
func writePid() {
engine.Logger.Info(*pidFile)
f, err := os.Create(*pidFile)
if err != nil {
log.Fatal("Could not write pid file: ", err)
}
f.WriteString(strconv.Itoa(os.Getpid()))
if err := f.Close(); err != nil {
log.Fatal("Could not write pid file: ", err)
}
}
func main() {
flag.Parse()
if *version {
fmt.Println("CGRateS " + utils.VERSION)
return
}
if *pidFile != "" {
writePid()
}
if !*singlecpu {
runtime.GOMAXPROCS(runtime.NumCPU()) // For now it slows down computing due to CPU management, to be reviewed in future Go releases
}
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
cfg, err = config.NewCGRConfigFromFolder(*cfgDir)
if err != nil {
engine.Logger.Crit(fmt.Sprintf("Could not parse config: %s exiting!", err))
return
}
config.SetCgrConfig(cfg) // Share the config object
if *raterEnabled {
cfg.RaterEnabled = *raterEnabled
}
if *schedEnabled {
cfg.SchedulerEnabled = *schedEnabled
}
if *cdrsEnabled {
cfg.CDRSEnabled = *cdrsEnabled
}
var ratingDb engine.RatingStorage
var accountDb engine.AccountingStorage
var logDb engine.LogStorage
var loadDb engine.LoadStorage
var cdrDb engine.CdrStorage
if cfg.RaterEnabled || cfg.SchedulerEnabled { // 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
engine.Logger.Crit(fmt.Sprintf("Could not configure dataDb: %s exiting!", err))
return
}
defer ratingDb.Close()
engine.SetRatingStorage(ratingDb)
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
engine.Logger.Crit(fmt.Sprintf("Could not configure dataDb: %s exiting!", err))
return
}
defer accountDb.Close()
engine.SetAccountingStorage(accountDb)
}
if cfg.RaterEnabled || 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)
if err != nil { // Cannot configure logger database, show stopper
engine.Logger.Crit(fmt.Sprintf("Could not configure logger database: %s exiting!", err))
return
}
defer logDb.Close()
engine.SetStorageLogger(logDb)
// loadDb,cdrDb and logDb are all mapped on the same stordb storage
loadDb = logDb.(engine.LoadStorage)
cdrDb = logDb.(engine.CdrStorage)
engine.SetCdrStorage(cdrDb)
}
engine.SetRoundingDecimals(cfg.RoundingDecimals)
stopHandled := false
// Rpc/http server
server := new(engine.Server)
// Async starts here, will follow cgrates.json start order
exitChan := make(chan bool)
// Define internal connections via channels
internalBalancerChan := make(chan *balancer2go.Balancer, 1)
internalRaterChan := make(chan *engine.Responder, 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)
internalPubSubSChan := make(chan engine.PublisherSubscriber, 1)
internalUserSChan := make(chan engine.UserService, 1)
internalAliaseSChan := make(chan engine.AliasService, 1)
// Start balancer service
if cfg.BalancerEnabled {
go startBalancer(internalBalancerChan, &stopHandled, exitChan) // Not really needed async here but to cope with uniformity
}
// Start rater service
if cfg.RaterEnabled {
go startRater(internalRaterChan, internalBalancerChan, internalSchedulerChan, internalCdrStatSChan, internalHistorySChan, internalPubSubSChan, internalUserSChan, internalAliaseSChan,
server, ratingDb, accountDb, loadDb, cdrDb, logDb, &stopHandled, exitChan)
}
// Start Scheduler
if cfg.SchedulerEnabled {
go startScheduler(internalSchedulerChan, ratingDb, exitChan)
}
// Start CDR Server
if cfg.CDRSEnabled {
go startCDRS(internalCdrSChan, logDb, cdrDb, internalRaterChan, internalPubSubSChan, internalUserSChan, internalAliaseSChan, internalCdrStatSChan, server, exitChan)
}
// Start CDR Stats server
if cfg.CDRStatsEnabled {
go startCdrStats(internalCdrStatSChan, ratingDb, accountDb, server)
}
// Start CDRC components if necessary
go startCdrcs(internalCdrSChan, internalRaterChan, exitChan)
// Start SM-FreeSWITCH
if cfg.SmFsConfig.Enabled {
go startSmFreeSWITCH(internalRaterChan, cdrDb, exitChan)
// close all sessions on shutdown
go shutdownSessionmanagerSingnalHandler(exitChan)
}
// Start SM-Kamailio
if cfg.SmKamConfig.Enabled {
go startSmKamailio(internalRaterChan, cdrDb, exitChan)
}
// Start SM-OpenSIPS
if cfg.SmOsipsConfig.Enabled {
go startSmOpenSIPS(internalRaterChan, cdrDb, exitChan)
}
// Register session manager service // FixMe: make sure this is thread safe
if cfg.SmFsConfig.Enabled || cfg.SmKamConfig.Enabled || cfg.SmOsipsConfig.Enabled { // Register SessionManagerV1 service
smRpc = new(v1.SessionManagerV1)
server.RpcRegister(smRpc)
}
// Start HistoryS service
if cfg.HistoryServerEnabled {
go startHistoryServer(internalHistorySChan, server, exitChan)
}
// Start PubSubS service
if cfg.PubSubServerEnabled {
go startPubSubServer(internalPubSubSChan, accountDb, server)
}
// Start Aliases service
if cfg.AliasesServerEnabled {
go startAliasesServer(internalAliaseSChan, accountDb, server, exitChan)
}
// Start users service
if cfg.UserServerEnabled {
go startUsersServer(internalUserSChan, accountDb, server, exitChan)
}
// Serve rpc connections
go startRpc(server, internalRaterChan, internalCdrSChan, internalCdrStatSChan, internalHistorySChan,
internalPubSubSChan, internalUserSChan, internalAliaseSChan)
<-exitChan
if *pidFile != "" {
if err := os.Remove(*pidFile); err != nil {
engine.Logger.Warning("Could not remove pid file: " + err.Error())
}
}
engine.Logger.Info("Stopped all components. CGRateS shutdown!")
}

View File

@@ -1,6 +1,6 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2013 ITsysCOM
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

250
cmd/cgr-engine/rater.go Normal file
View File

@@ -0,0 +1,250 @@
/*
Real-time Charging System 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 main
import (
"fmt"
"time"
"github.com/cgrates/cgrates/apier/v1"
"github.com/cgrates/cgrates/apier/v2"
"github.com/cgrates/cgrates/balancer2go"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/history"
"github.com/cgrates/cgrates/scheduler"
"github.com/cgrates/cgrates/utils"
)
func startBalancer(internalBalancerChan chan *balancer2go.Balancer, stopHandled *bool, exitChan chan bool) {
bal := balancer2go.NewBalancer()
go stopBalancerSignalHandler(bal, exitChan)
*stopHandled = true
internalBalancerChan <- bal
}
// Starts rater and reports on chan
func startRater(internalRaterChan chan *engine.Responder, internalBalancerChan chan *balancer2go.Balancer, internalSchedulerChan chan *scheduler.Scheduler,
internalCdrStatSChan chan engine.StatsInterface, internalHistorySChan chan history.Scribe,
internalPubSubSChan chan engine.PublisherSubscriber, internalUserSChan chan engine.UserService, internalAliaseSChan chan engine.AliasService,
server *engine.Server,
ratingDb engine.RatingStorage, accountDb engine.AccountingStorage, loadDb engine.LoadStorage, cdrDb engine.CdrStorage, logDb engine.LogStorage,
stopHandled *bool, exitChan chan bool) {
waitTasks := make([]chan struct{}, 0)
//Cache load
cacheTaskChan := make(chan struct{})
waitTasks = append(waitTasks, cacheTaskChan)
go func() {
defer close(cacheTaskChan)
if err := ratingDb.CacheRatingAll(); err != nil {
engine.Logger.Crit(fmt.Sprintf("Cache rating error: %s", err.Error()))
exitChan <- true
return
}
if err := accountDb.CacheAccountingPrefixes(); err != nil { // Used to cache load history
engine.Logger.Crit(fmt.Sprintf("Cache accounting error: %s", err.Error()))
exitChan <- true
return
}
}()
// Retrieve scheduler for it's API methods
var sched *scheduler.Scheduler // Need the scheduler in APIer
if cfg.SchedulerEnabled {
schedTaskChan := make(chan struct{})
waitTasks = append(waitTasks, schedTaskChan)
go func() {
defer close(schedTaskChan)
select {
case sched = <-internalSchedulerChan:
internalSchedulerChan <- sched
case <-time.After(cfg.InternalTtl):
engine.Logger.Crit("<Rater>: Internal scheduler connection timeout.")
exitChan <- true
return
}
}()
}
// Connection to balancer
var bal *balancer2go.Balancer
if cfg.RaterBalancer != "" {
balTaskChan := make(chan struct{})
waitTasks = append(waitTasks, balTaskChan)
go func() {
defer close(balTaskChan)
if cfg.RaterBalancer == utils.INTERNAL {
select {
case bal = <-internalBalancerChan:
internalBalancerChan <- bal // Put it back if someone else is interested about
case <-time.After(cfg.InternalTtl):
engine.Logger.Crit("<Rater>: Internal balancer connection timeout.")
exitChan <- true
return
}
} else {
go registerToBalancer(exitChan)
go stopRaterSignalHandler(internalCdrStatSChan, exitChan)
*stopHandled = true
}
}()
}
// Connection to CDRStats
var cdrStats engine.StatsInterface
if cfg.RaterCdrStats != "" {
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):
engine.Logger.Crit("<Rater>: Internal cdrstats connection timeout.")
exitChan <- true
return
}
} else if cdrStats, err = engine.NewProxyStats(cfg.RaterCdrStats, cfg.ConnectAttempts, -1); err != nil {
engine.Logger.Crit(fmt.Sprintf("<Rater> Could not connect to cdrstats, error: %s", err.Error()))
exitChan <- true
return
}
}()
}
// Connection to HistoryS
if cfg.RaterHistoryServer != "" {
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):
engine.Logger.Crit("<Rater>: Internal historys connection timeout.")
exitChan <- true
return
}
} else if scribeServer, err = history.NewProxyScribe(cfg.RaterHistoryServer, cfg.ConnectAttempts, -1); err != nil {
engine.Logger.Crit(fmt.Sprintf("<Rater> Could not connect historys, error: %s", err.Error()))
exitChan <- true
return
}
engine.SetHistoryScribe(scribeServer) // ToDo: replace package sharing with connection based one
}()
}
// Connection to pubsubs
if cfg.RaterPubSubServer != "" {
pubsubTaskChan := make(chan struct{})
waitTasks = append(waitTasks, pubsubTaskChan)
go func() {
defer close(pubsubTaskChan)
var pubSubServer engine.PublisherSubscriber
if cfg.RaterPubSubServer == utils.INTERNAL {
select {
case pubSubServer = <-internalPubSubSChan:
internalPubSubSChan <- pubSubServer
case <-time.After(cfg.InternalTtl):
engine.Logger.Crit("<Rater>: Internal pubsub connection timeout.")
exitChan <- true
return
}
} else if pubSubServer, err = engine.NewProxyPubSub(cfg.RaterPubSubServer, cfg.ConnectAttempts, -1); err != nil {
engine.Logger.Crit(fmt.Sprintf("<Rater> Could not connect to pubsubs: %s", err.Error()))
exitChan <- true
return
}
engine.SetPubSub(pubSubServer) // ToDo: replace package sharing with connection based one
}()
}
// Connection to AliasService
if cfg.RaterAliasesServer != "" {
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):
engine.Logger.Crit("<Rater>: Internal aliases connection timeout.")
exitChan <- true
return
}
} else if aliasesServer, err = engine.NewProxyAliasService(cfg.RaterAliasesServer, cfg.ConnectAttempts, -1); err != nil {
engine.Logger.Crit(fmt.Sprintf("<Rater> Could not connect to aliases, error: %s", err.Error()))
exitChan <- true
return
}
engine.SetAliasService(aliasesServer) // ToDo: replace package sharing with connection based one
}()
}
// Connection to UserService
var userServer engine.UserService
if cfg.RaterUserServer != "" {
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):
engine.Logger.Crit("<Rater>: Internal users connection timeout.")
exitChan <- true
return
}
} else if userServer, err = engine.NewProxyUserService(cfg.RaterUserServer, cfg.ConnectAttempts, -1); err != nil {
engine.Logger.Crit(fmt.Sprintf("<Rater> Could not connect users, error: %s", err.Error()))
exitChan <- true
return
}
engine.SetUserService(userServer)
}()
}
// Wait for all connections to complete before going further
for _, chn := range waitTasks {
<-chn
}
responder := &engine.Responder{Bal: bal, ExitChan: exitChan, Stats: cdrStats}
apierRpcV1 := &v1.ApierV1{StorDb: loadDb, RatingDb: ratingDb, AccountDb: accountDb, CdrDb: cdrDb, LogDb: logDb, Sched: sched,
Config: cfg, Responder: responder, CdrStatsSrv: cdrStats, Users: userServer}
apierRpcV2 := &v2.ApierV2{
ApierV1: *apierRpcV1}
// internalSchedulerChan shared here
server.RpcRegister(responder)
server.RpcRegister(apierRpcV1)
server.RpcRegister(apierRpcV2)
internalRaterChan <- responder // Rater done
}

View File

@@ -1,6 +1,6 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2013 ITsysCOM
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
@@ -20,88 +20,111 @@ package main
import (
"fmt"
"github.com/cgrates/cgrates/rater"
"github.com/cgrates/cgrates/scheduler"
"net/rpc"
"os"
"os/signal"
"syscall"
"github.com/cgrates/cgrates/balancer2go"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/scheduler"
)
/*
Listens for SIGTERM, SIGINT, SIGQUIT system signals and shuts down all the registered raters.
Listens for SIGTERM, SIGINT, SIGQUIT system signals and shuts down all the registered engines.
*/
func stopBalancerSingnalHandler() {
func stopBalancerSignalHandler(bal *balancer2go.Balancer, exitChan chan bool) {
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
sig := <-c
engine.Logger.Info(fmt.Sprintf("Caught signal %v, sending shutdown to engines\n", sig))
bal.Shutdown("Responder.Shutdown")
exitChan <- true
}
func generalSignalHandler(internalCdrStatSChan chan engine.StatsInterface, exitChan chan bool) {
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
sig := <-c
rater.Logger.Info(fmt.Sprintf("Caught signal %v, sending shutdownto raters\n", sig))
bal.Shutdown("Responder.Shutdown")
engine.Logger.Info(fmt.Sprintf("Caught signal %v, shuting down cgr-engine\n", sig))
var dummyInt int
select {
case cdrStats := <-internalCdrStatSChan:
cdrStats.Stop(dummyInt, &dummyInt)
default:
}
exitChan <- true
}
/*
Listens for the SIGTERM, SIGINT, SIGQUIT system signals and gracefuly unregister from balancer and closes the storage before exiting.
*/
func stopRaterSingnalHandler() {
func stopRaterSignalHandler(internalCdrStatSChan chan engine.StatsInterface, exitChan chan bool) {
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
sig := <-c
rater.Logger.Info(fmt.Sprintf("Caught signal %v, unregistering from balancer\n", sig))
unregisterFromBalancer()
engine.Logger.Info(fmt.Sprintf("Caught signal %v, unregistering from balancer\n", sig))
unregisterFromBalancer(exitChan)
var dummyInt int
select {
case cdrStats := <-internalCdrStatSChan:
cdrStats.Stop(dummyInt, &dummyInt)
default:
}
exitChan <- true
}
/*
Connects to the balancer and calls unregister RPC method.
*/
func unregisterFromBalancer() {
func unregisterFromBalancer(exitChan chan bool) {
client, err := rpc.Dial("tcp", cfg.RaterBalancer)
if err != nil {
rater.Logger.Crit("Cannot contact the balancer!")
engine.Logger.Crit("Cannot contact the balancer!")
exitChan <- true
return
}
var reply int
rater.Logger.Info(fmt.Sprintf("Unregistering from balancer %s", cfg.RaterBalancer))
client.Call("Responder.UnRegisterRater", cfg.RaterListen, &reply)
engine.Logger.Info(fmt.Sprintf("Unregistering from balancer %s", cfg.RaterBalancer))
client.Call("Responder.UnRegisterRater", cfg.RPCGOBListen, &reply)
if err := client.Close(); err != nil {
rater.Logger.Crit("Could not close balancer unregistration!")
engine.Logger.Crit("Could not close balancer unregistration!")
exitChan <- true
}
}
/*
Connects to the balancer and rehisters the rater to the server.
Connects to the balancer and rehisters the engine to the server.
*/
func registerToBalancer() {
func registerToBalancer(exitChan chan bool) {
client, err := rpc.Dial("tcp", cfg.RaterBalancer)
if err != nil {
rater.Logger.Crit(fmt.Sprintf("Cannot contact the balancer: %v", err))
engine.Logger.Crit(fmt.Sprintf("Cannot contact the balancer: %v", err))
exitChan <- true
return
}
var reply int
rater.Logger.Info(fmt.Sprintf("Registering to balancer %s", cfg.RaterBalancer))
client.Call("Responder.RegisterRater", cfg.RaterListen, &reply)
engine.Logger.Info(fmt.Sprintf("Registering to balancer %s", cfg.RaterBalancer))
client.Call("Responder.RegisterRater", cfg.RPCGOBListen, &reply)
if err := client.Close(); err != nil {
rater.Logger.Crit("Could not close balancer registration!")
engine.Logger.Crit("Could not close balancer registration!")
exitChan <- true
}
rater.Logger.Info("Registration finished!")
engine.Logger.Info("Registration finished!")
}
// Listens for the HUP system signal and gracefuly reloads the timers from database.
func reloadSchedulerSingnalHandler(sched *scheduler.Scheduler, getter rater.DataStorage) {
func reloadSchedulerSingnalHandler(sched *scheduler.Scheduler, getter engine.RatingStorage) {
for {
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGHUP)
sig := <-c
rater.Logger.Info(fmt.Sprintf("Caught signal %v, reloading action timings.\n", sig))
sched.LoadActionTimings(getter)
engine.Logger.Info(fmt.Sprintf("Caught signal %v, reloading action timings.\n", sig))
sched.LoadActionPlans(getter)
// check the tip of the queue for new actions
sched.Restart()
}
@@ -110,13 +133,15 @@ func reloadSchedulerSingnalHandler(sched *scheduler.Scheduler, getter rater.Data
/*
Listens for the SIGTERM, SIGINT, SIGQUIT system signals and shuts down the session manager.
*/
func shutdownSessionmanagerSingnalHandler() {
func shutdownSessionmanagerSingnalHandler(exitChan chan bool) {
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
<-c
if err := sm.Shutdown(); err != nil {
rater.Logger.Warning(fmt.Sprintf("<SessionManager> %s", err))
for _, sm := range sms {
if err := sm.Shutdown(); err != nil {
engine.Logger.Warning(fmt.Sprintf("<SessionManager> %s", err))
}
}
exitChan <- true
}

View File

@@ -1,6 +1,6 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2013 ITsysCOM
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
@@ -21,155 +21,285 @@ package main
import (
"flag"
"fmt"
"github.com/cgrates/cgrates/rater"
"log"
"net/rpc"
"path"
"regexp"
"strconv"
)
const (
POSTGRES = "postgres"
MONGO = "mongo"
REDIS = "redis"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/history"
"github.com/cgrates/cgrates/utils"
)
var (
//separator = flag.String("separator", ",", "Default field separator")
db_type = flag.String("dbtype", REDIS, "The type of the database (redis|mongo|postgres)")
db_host = flag.String("dbhost", "localhost", "The database host to connect to.")
db_port = flag.String("dbport", "6379", "The database port to bind to.")
db_name = flag.String("dbname", "10", "he name/number of the database to connect to.")
db_user = flag.String("dbuser", "", "The database user to sign in as.")
db_pass = flag.String("dbpass", "", "The database user's password.")
cgrConfig, _ = config.NewDefaultCGRConfig()
tpdb_type = flag.String("tpdb_type", cgrConfig.TpDbType, "The type of the TariffPlan database <redis>")
tpdb_host = flag.String("tpdb_host", cgrConfig.TpDbHost, "The TariffPlan host to connect to.")
tpdb_port = flag.String("tpdb_port", cgrConfig.TpDbPort, "The TariffPlan port to bind to.")
tpdb_name = flag.String("tpdb_name", cgrConfig.TpDbName, "The name/number of the TariffPlan to connect to.")
tpdb_user = flag.String("tpdb_user", cgrConfig.TpDbUser, "The TariffPlan user to sign in as.")
tpdb_pass = flag.String("tpdb_passwd", cgrConfig.TpDbPass, "The TariffPlan user's password.")
flush = flag.Bool("flush", false, "Flush the database before importing")
dataPath = flag.String("path", ".", "The path containing the data files")
version = flag.Bool("version", false, "Prints the application version.")
datadb_type = flag.String("datadb_type", cgrConfig.DataDbType, "The type of the DataDb database <redis>")
datadb_host = flag.String("datadb_host", cgrConfig.DataDbHost, "The DataDb host to connect to.")
datadb_port = flag.String("datadb_port", cgrConfig.DataDbPort, "The DataDb port to bind to.")
datadb_name = flag.String("datadb_name", cgrConfig.DataDbName, "The name/number of the DataDb to connect to.")
datadb_user = flag.String("datadb_user", cgrConfig.DataDbUser, "The DataDb user to sign in as.")
datadb_pass = flag.String("datadb_passwd", cgrConfig.DataDbPass, "The DataDb user's password.")
destinationsFn = "Destinations.csv"
ratesFn = "Rates.csv"
timingsFn = "Timings.csv"
ratetimingsFn = "RateTimings.csv"
ratingprofilesFn = "RatingProfiles.csv"
actionsFn = "Actions.csv"
actiontimingsFn = "ActionTimings.csv"
actiontriggersFn = "ActionTriggers.csv"
accountactionsFn = "AccountActions.csv"
sep rune
stor_db_type = flag.String("stordb_type", cgrConfig.StorDBType, "The type of the storDb database <mysql>")
stor_db_host = flag.String("stordb_host", cgrConfig.StorDBHost, "The storDb host to connect to.")
stor_db_port = flag.String("stordb_port", cgrConfig.StorDBPort, "The storDb port to bind to.")
stor_db_name = flag.String("stordb_name", cgrConfig.StorDBName, "The name/number of the storDb to connect to.")
stor_db_user = flag.String("stordb_user", cgrConfig.StorDBUser, "The storDb user to sign in as.")
stor_db_pass = flag.String("stordb_passwd", cgrConfig.StorDBPass, "The storDb user's password.")
dbdata_encoding = flag.String("dbdata_encoding", cgrConfig.DBDataEncoding, "The encoding used to store object data in strings")
flush = flag.Bool("flushdb", false, "Flush the database before importing")
tpid = flag.String("tpid", "", "The tariff plan id from the database")
dataPath = flag.String("path", "./", "The path to folder containing the data files")
version = flag.Bool("version", false, "Prints the application version.")
verbose = flag.Bool("verbose", false, "Enable detailed verbose logging output")
dryRun = flag.Bool("dry_run", false, "When true will not save loaded data to dataDb but just parse it for consistency and errors.")
validate = flag.Bool("validate", false, "When true will run various check on the loaded data to check for structural errors")
stats = flag.Bool("stats", false, "Generates statsistics about given data.")
fromStorDb = flag.Bool("from_stordb", false, "Load the tariff plan from storDb to dataDb")
toStorDb = flag.Bool("to_stordb", false, "Import the tariff plan from files to storDb")
historyServer = flag.String("history_server", cgrConfig.RPCGOBListen, "The history server address:port, empty to disable automaticautomatic history archiving")
raterAddress = flag.String("rater_address", cgrConfig.RPCGOBListen, "Rater service to contact for cache reloads, empty to disable automatic cache reloads")
cdrstatsAddress = flag.String("cdrstats_address", cgrConfig.RPCGOBListen, "CDRStats service to contact for data reloads, empty to disable automatic data reloads")
usersAddress = flag.String("users_address", cgrConfig.RPCGOBListen, "Users service to contact for data reloads, empty to disable automatic data reloads")
runId = flag.String("runid", "", "Uniquely identify an import/load, postpended to some automatic fields")
loadHistorySize = flag.Int("load_history_size", cgrConfig.LoadHistorySize, "Limit the number of records in the load history")
timezone = flag.String("timezone", cgrConfig.DefaultTimezone, `Timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB>`)
)
type validator struct {
fn string
re *regexp.Regexp
message string
}
func main() {
flag.Parse()
if *version {
fmt.Println("CGRateS " + rater.VERSION)
fmt.Println("CGRateS " + utils.VERSION)
return
}
dataFilesValidators := []*validator{
&validator{destinationsFn,
regexp.MustCompile(`(?:\w+\s*,\s*){1}(?:\d+.?\d*){1}$`),
"Tag[0-9A-Za-z_],Prefix[0-9]"},
&validator{ratesFn,
regexp.MustCompile(`(?:\w+\s*,\s*){2}(?:\d+.?\d*,?){4}$`),
"Tag[0-9A-Za-z_],DestinationsTag[0-9A-Za-z_],ConnectFee[0-9.],Price[0-9.],PricedUnits[0-9.],RateIncrement[0-9.]"},
&validator{timingsFn,
regexp.MustCompile(`(?:\w+\s*,\s*){1}(?:\*all\s*,\s*|(?:\d{1,4};?)+\s*,\s*|\s*,\s*){4}(?:\d{2}:\d{2}:\d{2}|\*asap){1}$`),
"Tag[0-9A-Za-z_],Years[0-9;]|*all|<empty>,Months[0-9;]|*all|<empty>,MonthDays[0-9;]|*all|<empty>,WeekDays[0-9;]|*all|<empty>,Time[0-9:]|*asap(00:00:00)"},
&validator{ratetimingsFn,
regexp.MustCompile(`(?:\w+\s*,\s*){3}(?:\d+.?\d*){1}$`),
"Tag[0-9A-Za-z_],RatesTag[0-9A-Za-z_],TimingProfile[0-9A-Za-z_],Weight[0-9.]"},
&validator{ratingprofilesFn,
regexp.MustCompile(`(?:\w+\s*,\s*){1}(?:\d+\s*,\s*){1}(?:OUT\s*,\s*|IN\s*,\s*){1}(?:\*all\s*,\s*|[\w:\.]+\s*,\s*){1}(?:\w*\s*,\s*){1}(?:\w+\s*,\s*){1}(?:\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z){1}$`),
"Tenant[0-9A-Za-z_],TOR[0-9],Direction OUT|IN,Subject[0-9A-Za-z_:.]|*all,RatesFallbackSubject[0-9A-Za-z_]|<empty>,RatesTimingTag[0-9A-Za-z_],ActivationTime[[0-9T:X]] (2012-01-01T00:00:00Z)"},
&validator{actionsFn,
regexp.MustCompile(`(?:\w+\s*,\s*){3}(?:OUT\s*,\s*|IN\s*,\s*){1}(?:\d+\s*,\s*){1}(?:\w+\s*,\s*|\*all\s*,\s*){1}(?:ABSOLUTE\s*,\s*|PERCENT\s*,\s*|\s*,\s*){1}(?:\d*\.?\d*\s*,?\s*){3}$`),
"Tag[0-9A-Za-z_],Action[0-9A-Za-z_],BalanceTag[0-9A-Za-z_],Direction OUT|IN,Units[0-9],DestinationTag[0-9A-Za-z_]|*all,PriceType ABSOLUT|PERCENT,PriceValue[0-9.],MinutesWeight[0-9.],Weight[0-9.]"},
&validator{actiontimingsFn,
regexp.MustCompile(`(?:\w+\s*,\s*){3}(?:\d+\.?\d*){1}`),
"Tag[0-9A-Za-z_],ActionsTag[0-9A-Za-z_],TimingTag[0-9A-Za-z_],Weight[0-9.]"},
&validator{actiontriggersFn,
regexp.MustCompile(`(?:\w+\s*,\s*){1}(?:MONETARY\s*,\s*|SMS\s*,\s*|MINUTES\s*,\s*|INTERNET\s*,\s*|INTERNET_TIME\s*,\s*){1}(?:OUT\s*,\s*|IN\s*,\s*){1}(?:\d+\.?\d*\s*,\s*){1}(?:\w+\s*,\s*|\*all\s*,\s*){1}(?:\w+\s*,\s*){1}(?:\d+\.?\d*){1}$`),
"Tag[0-9A-Za-z_],BalanceTag MONETARY|SMS|MINUTES|INTERNET|INTERNET_TIME,Direction OUT|IN,ThresholdValue[0-9.],DestinationTag[0-9A-Za-z_]|*all,ActionsTag[0-9A-Za-z_],Weight[0-9.]"},
&validator{accountactionsFn,
regexp.MustCompile(`(?:\w+\s*,\s*){1}(?:[\w:.]+\s*,\s*){1}(?:OUT\s*,\s*|IN\s*,\s*){1}(?:\w+\s*,?\s*){2}$`),
"Tenant[0-9A-Za-z_],Account[0-9A-Za-z_:.],Direction OUT|IN,ActionTimingsTag[0-9A-Za-z_],ActionTriggersTag[0-9A-Za-z_]"},
var errRatingDb, errAccDb, errStorDb, err error
var ratingDb engine.RatingStorage
var accountDb engine.AccountingStorage
var storDb engine.LoadStorage
var rater, cdrstats, users *rpc.Client
var loader engine.LoadReader
// Init necessary db connections, only if not already
if !*dryRun { // make sure we do not need db connections on dry run, also not importing into any stordb
if *fromStorDb {
ratingDb, errRatingDb = engine.ConfigureRatingStorage(*tpdb_type, *tpdb_host, *tpdb_port, *tpdb_name,
*tpdb_user, *tpdb_pass, *dbdata_encoding)
accountDb, errAccDb = engine.ConfigureAccountingStorage(*datadb_type, *datadb_host, *datadb_port, *datadb_name, *datadb_user, *datadb_pass, *dbdata_encoding)
storDb, errStorDb = engine.ConfigureLoadStorage(*stor_db_type, *stor_db_host, *stor_db_port, *stor_db_name, *stor_db_user, *stor_db_pass, *dbdata_encoding,
cgrConfig.StorDBMaxOpenConns, cgrConfig.StorDBMaxIdleConns)
} else if *toStorDb { // Import from csv files to storDb
storDb, errStorDb = engine.ConfigureLoadStorage(*stor_db_type, *stor_db_host, *stor_db_port, *stor_db_name, *stor_db_user, *stor_db_pass, *dbdata_encoding,
cgrConfig.StorDBMaxOpenConns, cgrConfig.StorDBMaxIdleConns)
} else { // Default load from csv files to dataDb
ratingDb, errRatingDb = engine.ConfigureRatingStorage(*tpdb_type, *tpdb_host, *tpdb_port, *tpdb_name,
*tpdb_user, *tpdb_pass, *dbdata_encoding)
accountDb, errAccDb = engine.ConfigureAccountingStorage(*datadb_type, *datadb_host, *datadb_port, *datadb_name, *datadb_user, *datadb_pass, *dbdata_encoding)
}
// Defer databases opened to be closed when we are done
for _, db := range []engine.Storage{ratingDb, accountDb, storDb} {
if db != nil {
defer db.Close()
}
}
// Stop on db errors
for _, err = range []error{errRatingDb, errAccDb, errStorDb} {
if err != nil {
log.Fatalf("Could not open database connection: %v", err)
}
}
if *toStorDb { // Import files from a directory into storDb
if *tpid == "" {
log.Fatal("TPid required, please define it via *-tpid* command argument.")
}
csvImporter := engine.TPCSVImporter{
TPid: *tpid,
StorDb: storDb,
DirPath: *dataPath,
Sep: ',',
Verbose: *verbose,
ImportId: *runId,
}
if errImport := csvImporter.Run(); errImport != nil {
log.Fatal(errImport)
}
return
}
}
for _, v := range dataFilesValidators {
err := rater.ValidateCSVData(path.Join(*dataPath, v.fn), v.re)
if *fromStorDb { // Load Tariff Plan from storDb into dataDb
loader = storDb
} else { // Default load from csv files to dataDb
/*for fn, v := range engine.FileValidators {
err := engine.ValidateCSVData(path.Join(*dataPath, fn), v.Rule)
if err != nil {
log.Fatal(err, "\n\t", v.Message)
}
}*/
loader = engine.NewFileCSVStorage(',',
path.Join(*dataPath, utils.DESTINATIONS_CSV),
path.Join(*dataPath, utils.TIMINGS_CSV),
path.Join(*dataPath, utils.RATES_CSV),
path.Join(*dataPath, utils.DESTINATION_RATES_CSV),
path.Join(*dataPath, utils.RATING_PLANS_CSV),
path.Join(*dataPath, utils.RATING_PROFILES_CSV),
path.Join(*dataPath, utils.SHARED_GROUPS_CSV),
path.Join(*dataPath, utils.LCRS_CSV),
path.Join(*dataPath, utils.ACTIONS_CSV),
path.Join(*dataPath, utils.ACTION_PLANS_CSV),
path.Join(*dataPath, utils.ACTION_TRIGGERS_CSV),
path.Join(*dataPath, utils.ACCOUNT_ACTIONS_CSV),
path.Join(*dataPath, utils.DERIVED_CHARGERS_CSV),
path.Join(*dataPath, utils.CDR_STATS_CSV),
path.Join(*dataPath, utils.USERS_CSV),
path.Join(*dataPath, utils.ALIASES_CSV),
)
}
tpReader := engine.NewTpReader(ratingDb, accountDb, loader, *tpid, *timezone, *loadHistorySize)
err = tpReader.LoadAll()
if err != nil {
log.Fatal(err)
}
if *stats {
tpReader.ShowStatistics()
}
if *validate {
if !tpReader.IsValid() {
return
}
}
if *dryRun { // We were just asked to parse the data, not saving it
return
}
if *historyServer != "" { // Init scribeAgent so we can store the differences
if scribeAgent, err := history.NewProxyScribe(*historyServer, 3, 3); 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 {
engine.SetHistoryScribe(scribeAgent)
//defer scribeAgent.Client.Close()
}
} else {
log.Print("WARNING: Rates history archiving is disabled!")
}
if *raterAddress != "" { // Init connection to rater so we can reload it's data
rater, err = rpc.Dial("tcp", *raterAddress)
if err != nil {
log.Fatal(err, "\n\t", v.message)
log.Fatalf("Could not connect to rater: %s", err.Error())
return
}
} else {
log.Print("WARNING: Rates automatic cache reloading is disabled!")
}
//sep = []rune(*separator)[0]
sep = ','
csvr := rater.NewFileCSVReader()
err := csvr.LoadDestinations(path.Join(*dataPath, destinationsFn), sep)
if err != nil {
log.Fatal(err)
}
err = csvr.LoadRates(path.Join(*dataPath, ratesFn), sep)
if err != nil {
log.Fatal(err)
}
err = csvr.LoadTimings(path.Join(*dataPath, timingsFn), sep)
if err != nil {
log.Fatal(err)
}
err = csvr.LoadRateTimings(path.Join(*dataPath, ratetimingsFn), sep)
if err != nil {
log.Fatal(err)
}
err = csvr.LoadRatingProfiles(path.Join(*dataPath, ratingprofilesFn), sep)
if err != nil {
log.Fatal(err)
}
err = csvr.LoadActions(path.Join(*dataPath, actionsFn), sep)
if err != nil {
log.Fatal(err)
}
err = csvr.LoadActionTimings(path.Join(*dataPath, actiontimingsFn), sep)
if err != nil {
log.Fatal(err)
}
err = csvr.LoadActionTriggers(path.Join(*dataPath, actiontriggersFn), sep)
if err != nil {
log.Fatal(err)
}
err = csvr.LoadAccountActions(path.Join(*dataPath, accountactionsFn), sep)
if err != nil {
log.Fatal(err)
}
var getter rater.DataStorage
switch *db_type {
case REDIS:
db_nb, err := strconv.Atoi(*db_name)
if err != nil {
log.Fatal("Redis db name must be an integer!")
if *cdrstatsAddress != "" { // Init connection to rater so we can reload it's data
if *cdrstatsAddress == *raterAddress {
cdrstats = rater
} else {
cdrstats, err = rpc.Dial("tcp", *cdrstatsAddress)
if err != nil {
log.Fatalf("Could not connect to CDRStats API: %s", err.Error())
return
}
}
if *db_port != "" {
*db_host += ":" + *db_port
}
getter, err = rater.NewRedisStorage(*db_host, db_nb, *db_pass)
case MONGO:
getter, err = rater.NewMongoStorage(*db_host, *db_port, *db_name, *db_user, *db_pass)
case POSTGRES:
getter, err = rater.NewPostgresStorage(*db_host, *db_port, *db_name, *db_user, *db_pass)
default:
log.Fatal("Unknown data db type, exiting!")
} else {
log.Print("WARNING: CDRStats automatic data reload is disabled!")
}
if err != nil {
log.Fatalf("Could not open database connection: %v", err)
if *usersAddress != "" { // Init connection to rater so we can reload it's data
if *usersAddress == *raterAddress {
users = rater
} else {
users, err = rpc.Dial("tcp", *usersAddress)
if err != nil {
log.Fatalf("Could not connect to Users API: %s", err.Error())
return
}
}
} else {
log.Print("WARNING: Users automatic data reload is disabled!")
}
// write maps to database
if err := csvr.WriteToDatabase(getter, *flush, true); err != nil {
if err := tpReader.WriteToDatabase(*flush, *verbose); err != nil {
log.Fatal("Could not write to database: ", err)
}
if len(*historyServer) != 0 && *verbose {
log.Print("Wrote history.")
}
// Reload scheduler and cache
if rater != nil {
reply := ""
dstIds, _ := tpReader.GetLoadedIds(utils.DESTINATION_PREFIX)
rplIds, _ := tpReader.GetLoadedIds(utils.RATING_PLAN_PREFIX)
rpfIds, _ := tpReader.GetLoadedIds(utils.RATING_PROFILE_PREFIX)
actIds, _ := tpReader.GetLoadedIds(utils.ACTION_PREFIX)
shgIds, _ := tpReader.GetLoadedIds(utils.SHARED_GROUP_PREFIX)
aliases, _ := tpReader.GetLoadedIds(utils.ALIASES_PREFIX)
lcrIds, _ := tpReader.GetLoadedIds(utils.LCR_PREFIX)
dcs, _ := tpReader.GetLoadedIds(utils.DERIVEDCHARGERS_PREFIX)
// Reload cache first since actions could be calling info from within
if *verbose {
log.Print("Reloading cache")
}
if *flush {
dstIds, rplIds, rpfIds, lcrIds = nil, nil, nil, nil // Should reload all these on flush
}
if err = rater.Call("ApierV1.ReloadCache", utils.ApiReloadCache{
DestinationIds: dstIds,
RatingPlanIds: rplIds,
RatingProfileIds: rpfIds,
ActionIds: actIds,
SharedGroupIds: shgIds,
Aliases: aliases,
LCRIds: lcrIds,
DerivedChargers: dcs,
}, &reply); err != nil {
log.Printf("WARNING: Got error on cache reload: %s\n", err.Error())
}
actTmgIds, _ := tpReader.GetLoadedIds(utils.ACTION_TIMING_PREFIX)
if len(actTmgIds) != 0 {
if *verbose {
log.Print("Reloading scheduler")
}
if err = rater.Call("ApierV1.ReloadScheduler", "", &reply); err != nil {
log.Printf("WARNING: Got error on scheduler reload: %s\n", err.Error())
}
}
}
if cdrstats != nil {
statsQueueIds, _ := tpReader.GetLoadedIds(utils.CDR_STATS_PREFIX)
if *flush {
statsQueueIds = []string{} // Force reload all
}
if len(statsQueueIds) != 0 {
if *verbose {
log.Print("Reloading CDRStats data")
}
var reply string
if err := cdrstats.Call("CDRStatsV1.ReloadQueues", utils.AttrCDRStatsReloadQueues{StatsQueueIds: statsQueueIds}, &reply); err != nil {
log.Printf("WARNING: Failed reloading stat queues, error: %s\n", err.Error())
}
}
}
if users != nil {
userIds, _ := tpReader.GetLoadedIds(utils.USERS_PREFIX)
if len(userIds) > 0 {
if *verbose {
log.Print("Reloading Users data")
}
var reply string
if err := cdrstats.Call("UsersV1.ReloadUsers", "", &reply); err != nil {
log.Printf("WARNING: Failed reloading users data, error: %s\n", err.Error())
}
}
}
}

View File

@@ -1,328 +0,0 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2013 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 main
import (
"errors"
"flag"
"fmt"
"github.com/cgrates/cgrates/balancer2go"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/mediator"
"github.com/cgrates/cgrates/rater"
"github.com/cgrates/cgrates/scheduler"
"github.com/cgrates/cgrates/sessionmanager"
"io"
"net"
"net/rpc"
"net/rpc/jsonrpc"
"os"
"runtime"
"strconv"
"strings"
"time"
)
const (
DISABLED = "disabled"
INTERNAL = "internal"
JSON = "json"
GOB = "gob"
POSTGRES = "postgres"
MONGO = "mongo"
REDIS = "redis"
SAME = "same"
FS = "freeswitch"
)
var (
cfgPath = flag.String("config", "/etc/cgrates/cgrates.cfg", "Configuration file location.")
version = flag.Bool("version", false, "Prints the application version.")
bal = balancer2go.NewBalancer()
exitChan = make(chan bool)
sm sessionmanager.SessionManager
cfg *config.CGRConfig
err error
)
func listenToRPCRequests(rpcResponder interface{}, rpcAddress string, rpc_encoding string) {
l, err := net.Listen("tcp", rpcAddress)
if err != nil {
rater.Logger.Crit(fmt.Sprintf("<Rater> Could not listen to %v: %v", rpcAddress, err))
exitChan <- true
return
}
defer l.Close()
rater.Logger.Info(fmt.Sprintf("<Rater> Listening for incomming RPC requests on %v", l.Addr()))
rpc.Register(rpcResponder)
var serveFunc func(io.ReadWriteCloser)
if rpc_encoding == JSON {
serveFunc = jsonrpc.ServeConn
} else {
serveFunc = rpc.ServeConn
}
for {
conn, err := l.Accept()
if err != nil {
rater.Logger.Err(fmt.Sprintf("<Rater> Accept error: %v", conn))
continue
}
rater.Logger.Info(fmt.Sprintf("<Rater> New incoming connection: %v", conn.RemoteAddr()))
go serveFunc(conn)
}
}
func startMediator(responder *rater.Responder, loggerDb rater.DataStorage) {
var connector rater.Connector
if cfg.MediatorRater == INTERNAL {
connector = responder
} else {
var client *rpc.Client
var err error
if cfg.MediatorRPCEncoding == JSON {
client, err = jsonrpc.Dial("tcp", cfg.MediatorRater)
} else {
client, err = rpc.Dial("tcp", cfg.MediatorRater)
}
if err != nil {
rater.Logger.Crit(fmt.Sprintf("Could not connect to rater: %v", err))
exitChan <- true
}
connector = &rater.RPCClientConnector{Client: client}
}
if _, err := os.Stat(cfg.MediatorCDRInDir); err != nil {
rater.Logger.Crit(fmt.Sprintf("The input path for mediator does not exist: %v", cfg.MediatorCDRInDir))
exitChan <- true
}
if _, err := os.Stat(cfg.MediatorCDROutDir); err != nil {
rater.Logger.Crit(fmt.Sprintf("The output path for mediator does not exist: %v", cfg.MediatorCDROutDir))
exitChan <- true
}
// ToDo: Why is here
// Check parsing errors
//if cfgParseErr != nil {
// rater.Logger.Crit(fmt.Sprintf("Errors on config parsing: <%v>", cfgParseErr))
// exitChan <- true
//}
m, err := mediator.NewMediator(connector, loggerDb, cfg.MediatorSkipDB, cfg.MediatorCDROutDir, cfg.MediatorPseudoprepaid,
cfg.FreeswitchDirectionIdx, cfg.FreeswitchTORIdx, cfg.FreeswitchTenantIdx, cfg.FreeswitchSubjectIdx, cfg.FreeswitchAccountIdx,
cfg.FreeswitchDestIdx, cfg.FreeswitchTimeStartIdx, cfg.FreeswitchDurationIdx, cfg.FreeswitchUUIDIdx)
if err != nil {
rater.Logger.Crit(fmt.Sprintf("Mediator config parsing error: %v", err))
exitChan <- true
}
m.TrackCDRFiles(cfg.MediatorCDRInDir)
}
func startSessionManager(responder *rater.Responder, loggerDb rater.DataStorage) {
var connector rater.Connector
if cfg.SMRater == INTERNAL {
connector = responder
} else {
var client *rpc.Client
var err error
if cfg.SMRPCEncoding == JSON {
client, err = jsonrpc.Dial("tcp", cfg.SMRater)
} else {
client, err = rpc.Dial("tcp", cfg.SMRater)
}
if err != nil {
rater.Logger.Crit(fmt.Sprintf("Could not connect to rater: %v", err))
exitChan <- true
}
connector = &rater.RPCClientConnector{Client: client}
}
switch cfg.SMSwitchType {
case FS:
dp, _ := time.ParseDuration(fmt.Sprintf("%vs", cfg.SMDebitInterval))
sm = sessionmanager.NewFSSessionManager(loggerDb, connector, dp)
errConn := sm.Connect(cfg)
if errConn != nil {
rater.Logger.Err(fmt.Sprintf("<SessionManager> error: %s!", errConn))
}
default:
rater.Logger.Err(fmt.Sprintf("<SessionManager> Unsupported session manger type: %s!", cfg.SMSwitchType))
exitChan <- true
}
exitChan <- true
}
func checkConfigSanity() {
if cfg.SMEnabled && cfg.RaterEnabled && cfg.RaterBalancer != DISABLED {
rater.Logger.Crit("The session manager must not be enabled on a worker rater (change [rater]/balancer to disabled)!")
exitChan <- true
}
if cfg.BalancerEnabled && cfg.RaterEnabled && cfg.RaterBalancer != DISABLED {
rater.Logger.Crit("The balancer is enabled so it cannot connect to anatoher balancer (change [rater]/balancer to disabled)!")
exitChan <- true
}
// check if the session manager or mediator is connectting via loopback
// if they are using the same encoding as the rater/balancer
// this scenariou should be used for debug puropses only (it is racy anyway)
// and it might be forbidden in the future
if strings.Contains(cfg.SMRater, "localhost") || strings.Contains(cfg.SMRater, "127.0.0.1") {
if cfg.BalancerEnabled {
if cfg.BalancerRPCEncoding != cfg.SMRPCEncoding {
rater.Logger.Crit("If you are connecting the session manager via the loopback to the balancer use the same type of rpc encoding!")
exitChan <- true
}
}
if cfg.RaterEnabled {
if cfg.RaterRPCEncoding != cfg.SMRPCEncoding {
rater.Logger.Crit("If you are connecting the session manager via the loopback to the arter use the same type of rpc encoding!")
exitChan <- true
}
}
}
if strings.Contains(cfg.MediatorRater, "localhost") || strings.Contains(cfg.MediatorRater, "127.0.0.1") {
if cfg.BalancerEnabled {
if cfg.BalancerRPCEncoding != cfg.MediatorRPCEncoding {
rater.Logger.Crit("If you are connecting the mediator via the loopback to the balancer use the same type of rpc encoding!")
exitChan <- true
}
}
if cfg.RaterEnabled {
if cfg.RaterRPCEncoding != cfg.MediatorRPCEncoding {
rater.Logger.Crit("If you are connecting the mediator via the loopback to the arter use the same type of rpc encoding!")
exitChan <- true
}
}
}
}
func configureDatabase(db_type, host, port, name, user, pass string) (getter rater.DataStorage, err error) {
switch db_type {
case REDIS:
db_nb, err := strconv.Atoi(name)
if err != nil {
rater.Logger.Crit("Redis db name must be an integer!")
exitChan <- true
}
if port != "" {
host += ":" + port
}
getter, err = rater.NewRedisStorage(host, db_nb, pass)
case MONGO:
getter, err = rater.NewMongoStorage(host, port, name, user, pass)
case POSTGRES:
getter, err = rater.NewPostgresStorage(host, port, name, user, pass)
default:
err = errors.New("unknown db")
rater.Logger.Crit("Unknown db type, exiting!")
exitChan <- true
}
if err != nil {
rater.Logger.Crit(fmt.Sprintf("Could not connect to db: %v, exiting!", err))
exitChan <- true
}
return
}
func main() {
flag.Parse()
if *version {
fmt.Println("CGRateS " + rater.VERSION)
return
}
runtime.GOMAXPROCS(runtime.NumCPU())
cfg, err = config.NewCGRConfig(cfgPath)
if err != nil {
rater.Logger.Crit(fmt.Sprintf("Could not parse config: %s exiting!", err))
return
}
// some consitency checks
go checkConfigSanity()
var getter, loggerDb rater.DataStorage
getter, err = configureDatabase(cfg.DataDBType, cfg.DataDBHost, cfg.DataDBPort, cfg.DataDBName, cfg.DataDBUser, cfg.DataDBPass)
if err != nil { // Cannot configure getter database, show stopper
rater.Logger.Crit(fmt.Sprintf("Could not configure database: %s exiting!", err))
return
}
defer getter.Close()
rater.SetDataStorage(getter)
if cfg.LogDBType == SAME {
loggerDb = getter
} else {
loggerDb, err = configureDatabase(cfg.LogDBType, cfg.LogDBHost, cfg.LogDBPort, cfg.LogDBName, cfg.LogDBUser, cfg.LogDBPass)
}
if err != nil { // Cannot configure logger database, show stopper
rater.Logger.Crit(fmt.Sprintf("Could not configure logger database: %s exiting!", err))
return
}
defer loggerDb.Close()
rater.SetStorageLogger(loggerDb)
if cfg.SMDebitInterval > 0 {
if dp, err := time.ParseDuration(fmt.Sprintf("%vs", cfg.SMDebitInterval)); err == nil {
rater.SetDebitPeriod(dp)
}
}
if cfg.RaterEnabled && cfg.RaterBalancer != DISABLED && !cfg.BalancerEnabled {
go registerToBalancer()
go stopRaterSingnalHandler()
}
responder := &rater.Responder{ExitChan: exitChan}
if cfg.RaterEnabled && !cfg.BalancerEnabled && cfg.RaterListen != INTERNAL {
rater.Logger.Info(fmt.Sprintf("Starting CGRateS Rater on %s.", cfg.RaterListen))
go listenToRPCRequests(responder, cfg.RaterListen, cfg.RaterRPCEncoding)
}
if cfg.BalancerEnabled {
rater.Logger.Info(fmt.Sprintf("Starting CGRateS Balancer on %s.", cfg.BalancerListen))
go stopBalancerSingnalHandler()
responder.Bal = bal
go listenToRPCRequests(responder, cfg.BalancerListen, cfg.BalancerRPCEncoding)
if cfg.RaterEnabled {
rater.Logger.Info("Starting internal rater.")
bal.AddClient("local", new(rater.ResponderWorker))
}
}
if cfg.SchedulerEnabled {
rater.Logger.Info("Starting CGRateS Scheduler.")
go func() {
sched := scheduler.NewScheduler()
go reloadSchedulerSingnalHandler(sched, getter)
sched.LoadActionTimings(getter)
sched.Loop()
}()
}
if cfg.SMEnabled {
rater.Logger.Info("Starting CGRateS SessionManager.")
go startSessionManager(responder, loggerDb)
// close all sessions on shutdown
go shutdownSessionmanagerSingnalHandler()
}
if cfg.MediatorEnabled {
rater.Logger.Info("Starting CGRateS Mediator.")
go startMediator(responder, loggerDb)
}
<-exitChan
rater.Logger.Info("Stopped all components. CGRateS shutdown!")
}

3
cmd/cgr-tester/README.md Normal file
View File

@@ -0,0 +1,3 @@
* Stress testing tools
All stress testing tools work with ../../data/tariffplans/fs_germany_prep1/. So start a rater and than issue a `cgr-loader -flushdb -verbose` in that directory.

View File

@@ -0,0 +1,189 @@
/*
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 main
import (
"flag"
"fmt"
"log"
"net/rpc"
"net/rpc/jsonrpc"
"os"
"runtime"
"runtime/pprof"
"time"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
var (
cgrConfig, _ = config.NewDefaultCGRConfig()
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
memprofile = flag.String("memprofile", "", "write memory profile to this file")
runs = flag.Int("runs", 10000, "stress cycle number")
parallel = flag.Int("parallel", 0, "run n requests in parallel")
ratingdb_type = flag.String("ratingdb_type", cgrConfig.TpDbType, "The type of the RatingDb database <redis>")
ratingdb_host = flag.String("ratingdb_host", cgrConfig.TpDbHost, "The RatingDb host to connect to.")
ratingdb_port = flag.String("ratingdb_port", cgrConfig.TpDbPort, "The RatingDb port to bind to.")
ratingdb_name = flag.String("ratingdb_name", cgrConfig.TpDbName, "The name/number of the RatingDb to connect to.")
ratingdb_user = flag.String("ratingdb_user", cgrConfig.TpDbUser, "The RatingDb user to sign in as.")
ratingdb_pass = flag.String("ratingdb_passwd", cgrConfig.TpDbPass, "The RatingDb user's password.")
accountdb_type = flag.String("accountdb_type", cgrConfig.DataDbType, "The type of the AccountingDb database <redis>")
accountdb_host = flag.String("accountdb_host", cgrConfig.DataDbHost, "The AccountingDb host to connect to.")
accountdb_port = flag.String("accountdb_port", cgrConfig.DataDbPort, "The AccountingDb port to bind to.")
accountdb_name = flag.String("accountdb_name", cgrConfig.DataDbName, "The name/number of the AccountingDb to connect to.")
accountdb_user = flag.String("accountdb_user", cgrConfig.DataDbUser, "The AccountingDb user to sign in as.")
accountdb_pass = flag.String("accountdb_passwd", cgrConfig.DataDbPass, "The AccountingDb user's password.")
dbdata_encoding = flag.String("dbdata_encoding", cgrConfig.DBDataEncoding, "The encoding used to store object data in strings.")
raterAddress = flag.String("rater_address", "", "Rater address for remote tests. Empty for internal rater.")
tor = flag.String("tor", utils.VOICE, "The type of record to use in queries.")
category = flag.String("category", "call", "The Record category to test.")
tenant = flag.String("tenant", "cgrates.org", "The type of record to use in queries.")
subject = flag.String("subject", "1001", "The rating subject to use in queries.")
destination = flag.String("destination", "1002", "The destination to use in queries.")
json = flag.Bool("json", false, "Use JSON RPC")
nilDuration = time.Duration(0)
)
func durInternalRater(cd *engine.CallDescriptor) (time.Duration, error) {
ratingDb, err := engine.ConfigureRatingStorage(*ratingdb_type, *ratingdb_host, *ratingdb_port, *ratingdb_name, *ratingdb_user, *ratingdb_pass, *dbdata_encoding)
if err != nil {
return nilDuration, fmt.Errorf("Could not connect to rating database: %s", err.Error())
}
defer ratingDb.Close()
engine.SetRatingStorage(ratingDb)
accountDb, err := engine.ConfigureAccountingStorage(*accountdb_type, *accountdb_host, *accountdb_port, *accountdb_name, *accountdb_user, *accountdb_pass, *dbdata_encoding)
if err != nil {
return nilDuration, fmt.Errorf("Could not connect to accounting database: %s", err.Error())
}
defer accountDb.Close()
engine.SetAccountingStorage(accountDb)
if err := ratingDb.CacheRatingAll(); err != nil {
return nilDuration, fmt.Errorf("Cache rating error: %s", err.Error())
}
if err := accountDb.CacheAccountingAll(); err != nil {
return nilDuration, fmt.Errorf("Cache accounting error: %s", err.Error())
}
log.Printf("Runnning %d cycles...", *runs)
var result *engine.CallCost
j := 0
start := time.Now()
for i := 0; i < *runs; i++ {
result, err = cd.GetCost()
if *memprofile != "" {
runtime.MemProfileRate = 1
runtime.GC()
f, err := os.Create(*memprofile)
if err != nil {
log.Fatal(err)
}
pprof.WriteHeapProfile(f)
f.Close()
break
}
j = i
}
log.Print(result, j, err)
memstats := new(runtime.MemStats)
runtime.ReadMemStats(memstats)
log.Printf("memstats before GC: Kbytes = %d footprint = %d",
memstats.HeapAlloc/1024, memstats.Sys/1024)
return time.Since(start), nil
}
func durRemoteRater(cd *engine.CallDescriptor) (time.Duration, error) {
result := engine.CallCost{}
var client *rpc.Client
var err error
if *json {
client, err = jsonrpc.Dial("tcp", *raterAddress)
} else {
client, err = rpc.Dial("tcp", *raterAddress)
}
if err != nil {
return nilDuration, fmt.Errorf("Could not connect to engine: %s", err.Error())
}
defer client.Close()
start := time.Now()
if *parallel > 0 {
// var divCall *rpc.Call
var sem = make(chan int, *parallel)
var finish = make(chan int)
for i := 0; i < *runs; i++ {
go func() {
sem <- 1
client.Call("Responder.GetCost", cd, &result)
<-sem
finish <- 1
// divCall = client.Go("Responder.GetCost", cd, &result, nil)
}()
}
for i := 0; i < *runs; i++ {
<-finish
}
// <-divCall.Done
} else {
for j := 0; j < *runs; j++ {
client.Call("Responder.GetCost", cd, &result)
}
}
log.Println(result)
return time.Since(start), nil
}
func main() {
flag.Parse()
runtime.GOMAXPROCS(runtime.NumCPU() - 1)
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
cd := &engine.CallDescriptor{
TimeStart: time.Date(2014, time.December, 11, 55, 30, 0, 0, time.UTC),
TimeEnd: time.Date(2014, time.December, 11, 55, 31, 0, 0, time.UTC),
DurationIndex: 60 * time.Second,
Direction: "*out",
TOR: *tor,
Category: *category,
Tenant: *tenant,
Subject: *subject,
Destination: *destination,
}
var duration time.Duration
var err error
if len(*raterAddress) == 0 {
duration, err = durInternalRater(cd)
} else {
duration, err = durRemoteRater(cd)
}
if err != nil {
log.Fatal(err.Error())
} else {
log.Printf("Elapsed: %d resulted: %f req/s.", duration, float64(*runs)/duration.Seconds())
}
}

View File

@@ -1,84 +0,0 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2013 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 main
import (
"flag"
"github.com/cgrates/cgrates/rater"
"log"
"net/rpc"
//"net/rpc/jsonrpc"
"net/rpc/jsonrpc"
"runtime"
"time"
)
var (
balancer = flag.String("balancer", "localhost:2001", "balancer server address")
runs = flag.Int("runs", 10000, "stress cycle number")
parallel = flag.Int("parallel", 0, "run n requests in parallel")
json = flag.Bool("json", false, "use JSON for RPC encoding")
)
func main() {
flag.Parse()
runtime.GOMAXPROCS(runtime.NumCPU())
t1 := time.Date(2012, time.February, 02, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 02, 18, 30, 0, 0, time.UTC)
cd := rater.CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
result := rater.CallCost{}
var client *rpc.Client
var err error
if *json {
client, err = jsonrpc.Dial("tcp", *balancer)
} else {
client, err = rpc.Dial("tcp", *balancer)
}
if err != nil {
log.Fatal("Could not connect to rater: ", err)
}
start := time.Now()
if *parallel > 0 {
// var divCall *rpc.Call
var sem = make(chan int, *parallel)
var finish = make(chan int)
for i := 0; i < *runs; i++ {
go func() {
sem <- 1
client.Call("Responder.GetCost", cd, &result)
<-sem
finish <- 1
// divCall = client.Go("Responder.GetCost", cd, &result, nil)
}()
}
for i := 0; i < *runs; i++ {
<-finish
}
// <-divCall.Done
} else {
for j := 0; j < *runs; j++ {
client.Call("Responder.GetCost", cd, &result)
}
}
duration := time.Since(start)
log.Println(result)
client.Close()
log.Printf("Elapsed: %v resulted: %v req/s.", duration, float64(*runs)/duration.Seconds())
}

View File

@@ -1,83 +0,0 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2013 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 main
import (
"flag"
"github.com/cgrates/cgrates/rater"
"log"
"os"
"runtime"
"runtime/pprof"
"time"
)
var (
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
memprofile = flag.String("memprofile", "", "write memory profile to this file")
runs = flag.Int("runs", 10000, "stress cycle number")
)
func main() {
flag.Parse()
runtime.GOMAXPROCS(runtime.NumCPU() - 1)
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
if *memprofile != "" {
f, err := os.Create(*memprofile)
if err != nil {
log.Fatal(err)
}
pprof.WriteHeapProfile(f)
f.Close()
return
}
t1 := time.Date(2012, time.February, 02, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 02, 18, 30, 0, 0, time.UTC)
cd := rater.CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
getter, err := rater.NewRedisStorage("localhost:6379", 10, "")
//getter, err := rater.NewMongoStorage("localhost", "cgrates")
defer getter.Close()
rater.SetDataStorage(getter)
log.Printf("Runnning %d cycles...", *runs)
var result *rater.CallCost
j := 0
start := time.Now()
for i := 0; i < *runs; i++ {
result, err = cd.GetCost()
j = i
}
duration := time.Since(start)
log.Print(result, j, err)
memstats := new(runtime.MemStats)
runtime.ReadMemStats(memstats)
log.Printf("memstats before GC: Kbytes = %d footprint = %d",
memstats.HeapAlloc/1024, memstats.Sys/1024)
log.Printf("Elapsed: %v resulted: %v req/s.", duration, float64(*runs)/duration.Seconds())
}

151
config/cdrcconfig.go Normal file
View File

@@ -0,0 +1,151 @@
/*
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 config
import (
"time"
"github.com/cgrates/cgrates/utils"
)
type CdrcConfig struct {
Enabled bool // Enable/Disable the profile
DryRun bool // Do not post CDRs to the server
Cdrs string // 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
Timezone string // timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB>
RunDelay time.Duration // Delay between runs, 0 for inotify driven requests
MaxOpenFiles int // Maximum number of files opened simultaneously
CdrInDir string // Folder to process CDRs from
CdrOutDir string // Folder to move processed CDRs to
FailedCallsPrefix string // Used in case of flatstore CDRs to avoid searching for BYE records
CdrSourceId string // Source identifier for the processed CDRs
CdrFilter utils.RSRFields // Filter CDR records to import
PartialRecordCache time.Duration // Duration to cache partial records when not pairing
HeaderFields []*CfgCdrField
ContentFields []*CfgCdrField
TrailerFields []*CfgCdrField
}
func (self *CdrcConfig) loadFromJsonCfg(jsnCfg *CdrcJsonCfg) error {
if jsnCfg == nil {
return nil
}
var err error
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.Cdr_format != nil {
self.CdrFormat = *jsnCfg.Cdr_format
}
if jsnCfg.Field_separator != nil && len(*jsnCfg.Field_separator) > 0 {
sepStr := *jsnCfg.Field_separator
self.FieldSeparator = rune(sepStr[0])
}
if jsnCfg.Data_usage_multiply_factor != nil {
self.DataUsageMultiplyFactor = *jsnCfg.Data_usage_multiply_factor
}
if jsnCfg.Timezone != nil {
self.Timezone = *jsnCfg.Timezone
}
if jsnCfg.Run_delay != nil {
self.RunDelay = time.Duration(*jsnCfg.Run_delay) * time.Second
}
if jsnCfg.Max_open_files != nil {
self.MaxOpenFiles = *jsnCfg.Max_open_files
}
if jsnCfg.Cdr_in_dir != nil {
self.CdrInDir = *jsnCfg.Cdr_in_dir
}
if jsnCfg.Cdr_out_dir != nil {
self.CdrOutDir = *jsnCfg.Cdr_out_dir
}
if jsnCfg.Failed_calls_prefix != nil {
self.FailedCallsPrefix = *jsnCfg.Failed_calls_prefix
}
if jsnCfg.Cdr_source_id != nil {
self.CdrSourceId = *jsnCfg.Cdr_source_id
}
if jsnCfg.Cdr_filter != nil {
if self.CdrFilter, err = utils.ParseRSRFields(*jsnCfg.Cdr_filter, utils.INFIELD_SEP); err != nil {
return err
}
}
if jsnCfg.Partial_record_cache != nil {
if self.PartialRecordCache, err = utils.ParseDurationWithSecs(*jsnCfg.Partial_record_cache); err != nil {
return err
}
}
if jsnCfg.Header_fields != nil {
if self.HeaderFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*jsnCfg.Header_fields); err != nil {
return err
}
}
if jsnCfg.Content_fields != nil {
if self.ContentFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*jsnCfg.Content_fields); err != nil {
return err
}
}
if jsnCfg.Trailer_fields != nil {
if self.TrailerFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*jsnCfg.Trailer_fields); err != nil {
return err
}
}
return nil
}
// Clone itself into a new CdrcConfig
func (self *CdrcConfig) Clone() *CdrcConfig {
clnCdrc := new(CdrcConfig)
clnCdrc.Enabled = self.Enabled
clnCdrc.Cdrs = self.Cdrs
clnCdrc.CdrFormat = self.CdrFormat
clnCdrc.FieldSeparator = self.FieldSeparator
clnCdrc.DataUsageMultiplyFactor = self.DataUsageMultiplyFactor
clnCdrc.Timezone = self.Timezone
clnCdrc.RunDelay = self.RunDelay
clnCdrc.MaxOpenFiles = self.MaxOpenFiles
clnCdrc.CdrInDir = self.CdrInDir
clnCdrc.CdrOutDir = self.CdrOutDir
clnCdrc.CdrSourceId = self.CdrSourceId
clnCdrc.HeaderFields = make([]*CfgCdrField, len(self.HeaderFields))
clnCdrc.ContentFields = make([]*CfgCdrField, len(self.ContentFields))
clnCdrc.TrailerFields = make([]*CfgCdrField, len(self.TrailerFields))
for idx, fld := range self.HeaderFields {
clonedVal := *fld
clnCdrc.HeaderFields[idx] = &clonedVal
}
for idx, fld := range self.ContentFields {
clonedVal := *fld
clnCdrc.ContentFields[idx] = &clonedVal
}
for idx, fld := range self.TrailerFields {
clonedVal := *fld
clnCdrc.TrailerFields[idx] = &clonedVal
}
return clnCdrc
}

19
config/cdrcconfig_test.go Normal file
View File

@@ -0,0 +1,19 @@
/*
Real-time Charging System 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 config

126
config/cdreconfig.go Normal file
View File

@@ -0,0 +1,126 @@
/*
Real-time Charging System 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 config
// One instance of CdrExporter
type CdreConfig struct {
CdrFormat string
FieldSeparator rune
DataUsageMultiplyFactor float64
SmsUsageMultiplyFactor float64
GenericUsageMultiplyFactor float64
CostMultiplyFactor float64
CostRoundingDecimals int
CostShiftDigits int
MaskDestId string
MaskLength int
ExportDir string
HeaderFields []*CfgCdrField
ContentFields []*CfgCdrField
TrailerFields []*CfgCdrField
}
func (self *CdreConfig) loadFromJsonCfg(jsnCfg *CdreJsonCfg) error {
if jsnCfg == nil {
return nil
}
var err error
if jsnCfg.Cdr_format != nil {
self.CdrFormat = *jsnCfg.Cdr_format
}
if jsnCfg.Field_separator != nil && len(*jsnCfg.Field_separator) > 0 { // Make sure we got at least one character so we don't get panic here
sepStr := *jsnCfg.Field_separator
self.FieldSeparator = rune(sepStr[0])
}
if jsnCfg.Data_usage_multiply_factor != nil {
self.DataUsageMultiplyFactor = *jsnCfg.Data_usage_multiply_factor
}
if jsnCfg.Sms_usage_multiply_factor != nil {
self.SmsUsageMultiplyFactor = *jsnCfg.Sms_usage_multiply_factor
}
if jsnCfg.Generic_usage_multiply_factor != nil {
self.GenericUsageMultiplyFactor = *jsnCfg.Generic_usage_multiply_factor
}
if jsnCfg.Cost_multiply_factor != nil {
self.CostMultiplyFactor = *jsnCfg.Cost_multiply_factor
}
if jsnCfg.Cost_rounding_decimals != nil {
self.CostRoundingDecimals = *jsnCfg.Cost_rounding_decimals
}
if jsnCfg.Cost_shift_digits != nil {
self.CostShiftDigits = *jsnCfg.Cost_shift_digits
}
if jsnCfg.Mask_destination_id != nil {
self.MaskDestId = *jsnCfg.Mask_destination_id
}
if jsnCfg.Mask_length != nil {
self.MaskLength = *jsnCfg.Mask_length
}
if jsnCfg.Export_dir != nil {
self.ExportDir = *jsnCfg.Export_dir
}
if jsnCfg.Header_fields != nil {
if self.HeaderFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*jsnCfg.Header_fields); err != nil {
return err
}
}
if jsnCfg.Content_fields != nil {
if self.ContentFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*jsnCfg.Content_fields); err != nil {
return err
}
}
if jsnCfg.Trailer_fields != nil {
if self.TrailerFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*jsnCfg.Trailer_fields); err != nil {
return err
}
}
return nil
}
// Clone itself into a new CdreConfig
func (self *CdreConfig) Clone() *CdreConfig {
clnCdre := new(CdreConfig)
clnCdre.CdrFormat = self.CdrFormat
clnCdre.FieldSeparator = self.FieldSeparator
clnCdre.DataUsageMultiplyFactor = self.DataUsageMultiplyFactor
clnCdre.SmsUsageMultiplyFactor = self.SmsUsageMultiplyFactor
clnCdre.GenericUsageMultiplyFactor = self.GenericUsageMultiplyFactor
clnCdre.CostMultiplyFactor = self.CostMultiplyFactor
clnCdre.CostRoundingDecimals = self.CostRoundingDecimals
clnCdre.CostShiftDigits = self.CostShiftDigits
clnCdre.MaskDestId = self.MaskDestId
clnCdre.MaskLength = self.MaskLength
clnCdre.ExportDir = self.ExportDir
clnCdre.HeaderFields = make([]*CfgCdrField, len(self.HeaderFields))
for idx, fld := range self.HeaderFields {
clonedVal := *fld
clnCdre.HeaderFields[idx] = &clonedVal
}
clnCdre.ContentFields = make([]*CfgCdrField, len(self.ContentFields))
for idx, fld := range self.ContentFields {
clonedVal := *fld
clnCdre.ContentFields[idx] = &clonedVal
}
clnCdre.TrailerFields = make([]*CfgCdrField, len(self.TrailerFields))
for idx, fld := range self.TrailerFields {
clonedVal := *fld
clnCdre.TrailerFields[idx] = &clonedVal
}
return clnCdre
}

97
config/cdreconfig_test.go Normal file
View File

@@ -0,0 +1,97 @@
/*
Real-time Charging System 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 config
import (
"github.com/cgrates/cgrates/utils"
"reflect"
"testing"
)
func TestCdreCfgClone(t *testing.T) {
cgrIdRsrs, _ := utils.ParseRSRFields("cgrid", utils.INFIELD_SEP)
runIdRsrs, _ := utils.ParseRSRFields("mediation_runid", utils.INFIELD_SEP)
emptyFields := []*CfgCdrField{}
initContentFlds := []*CfgCdrField{
&CfgCdrField{Tag: "CgrId",
Type: "cdrfield",
CdrFieldId: "cgrid",
Value: cgrIdRsrs},
&CfgCdrField{Tag: "RunId",
Type: "cdrfield",
CdrFieldId: "mediation_runid",
Value: runIdRsrs},
}
initCdreCfg := &CdreConfig{
CdrFormat: "csv",
FieldSeparator: rune(','),
DataUsageMultiplyFactor: 1.0,
CostMultiplyFactor: 1.0,
CostRoundingDecimals: -1,
CostShiftDigits: 0,
MaskDestId: "MASKED_DESTINATIONS",
MaskLength: 0,
ExportDir: "/var/log/cgrates/cdre",
ContentFields: initContentFlds,
}
eClnContentFlds := []*CfgCdrField{
&CfgCdrField{Tag: "CgrId",
Type: "cdrfield",
CdrFieldId: "cgrid",
Value: cgrIdRsrs},
&CfgCdrField{Tag: "RunId",
Type: "cdrfield",
CdrFieldId: "mediation_runid",
Value: runIdRsrs},
}
eClnCdreCfg := &CdreConfig{
CdrFormat: "csv",
FieldSeparator: rune(','),
DataUsageMultiplyFactor: 1.0,
CostMultiplyFactor: 1.0,
CostRoundingDecimals: -1,
CostShiftDigits: 0,
MaskDestId: "MASKED_DESTINATIONS",
MaskLength: 0,
ExportDir: "/var/log/cgrates/cdre",
HeaderFields: emptyFields,
ContentFields: eClnContentFlds,
TrailerFields: emptyFields,
}
clnCdreCfg := initCdreCfg.Clone()
if !reflect.DeepEqual(eClnCdreCfg, clnCdreCfg) {
t.Errorf("Cloned result: %+v", clnCdreCfg)
}
initCdreCfg.DataUsageMultiplyFactor = 1024.0
if !reflect.DeepEqual(eClnCdreCfg, clnCdreCfg) { // MOdifying a field after clone should not affect cloned instance
t.Errorf("Cloned result: %+v", clnCdreCfg)
}
initContentFlds[0].Tag = "Destination"
if !reflect.DeepEqual(eClnCdreCfg, clnCdreCfg) { // MOdifying a field after clone should not affect cloned instance
t.Errorf("Cloned result: %+v", clnCdreCfg)
}
clnCdreCfg.CostShiftDigits = 2
if initCdreCfg.CostShiftDigits != 0 {
t.Error("Unexpected CostShiftDigits: ", initCdreCfg.CostShiftDigits)
}
clnCdreCfg.ContentFields[0].CdrFieldId = "destination"
if initCdreCfg.ContentFields[0].CdrFieldId != "cgrid" {
t.Error("Unexpected change of CdrFieldId: ", initCdreCfg.ContentFields[0].CdrFieldId)
}
}

49
config/cdrstatsconfig.go Normal file
View File

@@ -0,0 +1,49 @@
/*
Real-time Charging System 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 config
import (
"time"
)
type CdrStatsConfig struct {
Id string // Config id, unique per config instance
QueueLength int // Number of items in the stats buffer
TimeWindow time.Duration // Will only keep the CDRs who's call setup time is not older than time.Now()-TimeWindow
SaveInterval time.Duration
Metrics []string // ASR, ACD, ACC
SetupInterval []time.Time // 2 or less items (>= start interval,< stop_interval)
TORs []string
CdrHosts []string
CdrSources []string
ReqTypes []string
Directions []string
Tenants []string
Categories []string
Accounts []string
Subjects []string
DestinationIds []string
UsageInterval []time.Duration // 2 or less items (>= Usage, <Usage)
Suppliers []string
DisconnectCauses []string
MediationRunIds []string
RatedAccounts []string
RatedSubjects []string
CostInterval []float64 // 2 or less items, (>=Cost, <Cost)
}

51
config/cfg_data.json Normal file
View File

@@ -0,0 +1,51 @@
{
// 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.
"general": {
"default_reqtype": "*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": {
"enabled": true, // enable Rater service: <true|false>
},
"cdrc": {
"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": {
"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,
"run_delay": 1,
"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
{"cdr_field_id": "tor", "value": "~7:s/^(voice|data|sms|generic)$/*$1/"},
{"cdr_field_id": "answer_time", "value": "1"},
{"cdr_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},
],
},
}

30
config/cfg_data2.json Normal file
View File

@@ -0,0 +1,30 @@
{
"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
{"cdr_field_id": "tor", "value": "~7:s/^(voice|data|sms|generic)$/*$1/"},
{"cdr_field_id": "answer_time", "value": "2"},
],
},
"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},
],
},
}

92
config/cfgcdrfield.go Normal file
View File

@@ -0,0 +1,92 @@
/*
Real-time Charging System 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 config
import (
"github.com/cgrates/cgrates/utils"
)
func NewCfgCdrFieldFromCdrFieldJsonCfg(jsnCfgFld *CdrFieldJsonCfg) (*CfgCdrField, error) {
var err error
cfgFld := new(CfgCdrField)
if jsnCfgFld.Tag != nil {
cfgFld.Tag = *jsnCfgFld.Tag
}
if jsnCfgFld.Type != nil {
cfgFld.Type = *jsnCfgFld.Type
}
if jsnCfgFld.Cdr_field_id != nil {
cfgFld.CdrFieldId = *jsnCfgFld.Cdr_field_id
}
if jsnCfgFld.Metatag_id != nil {
cfgFld.MetatagId = *jsnCfgFld.Metatag_id
}
if jsnCfgFld.Value != nil {
if cfgFld.Value, err = utils.ParseRSRFields(*jsnCfgFld.Value, utils.INFIELD_SEP); err != nil {
return nil, err
}
}
if jsnCfgFld.Field_filter != nil {
if cfgFld.FieldFilter, err = utils.ParseRSRFields(*jsnCfgFld.Field_filter, utils.INFIELD_SEP); err != nil {
return nil, err
}
}
if jsnCfgFld.Width != nil {
cfgFld.Width = *jsnCfgFld.Width
}
if jsnCfgFld.Strip != nil {
cfgFld.Strip = *jsnCfgFld.Strip
}
if jsnCfgFld.Padding != nil {
cfgFld.Padding = *jsnCfgFld.Padding
}
if jsnCfgFld.Layout != nil {
cfgFld.Layout = *jsnCfgFld.Layout
}
if jsnCfgFld.Mandatory != nil {
cfgFld.Mandatory = *jsnCfgFld.Mandatory
}
return cfgFld, nil
}
type CfgCdrField struct {
Tag string // Identifier for the administrator
Type string // Type of field
CdrFieldId string // StoredCdr field name
MetatagId string
Value utils.RSRFields
FieldFilter utils.RSRFields
Width int
Strip string
Padding string
Layout string
Mandatory bool
}
func CfgCdrFieldsFromCdrFieldsJsonCfg(jsnCfgFldss []*CdrFieldJsonCfg) ([]*CfgCdrField, error) {
retFields := make([]*CfgCdrField, len(jsnCfgFldss))
for idx, jsnFld := range jsnCfgFldss {
if cfgFld, err := NewCfgCdrFieldFromCdrFieldJsonCfg(jsnFld); err != nil {
return nil, err
} else {
retFields[idx] = cfgFld
}
}
return retFields, nil
}

View File

@@ -0,0 +1,19 @@
/*
Real-time Charging System 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 config

File diff suppressed because it is too large Load Diff

281
config/config_defaults.go Normal file
View File

@@ -0,0 +1,281 @@
/*
Real-time Charging System 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 config
const CGRATES_CFG_JSON = `
{
// 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.
"general": {
"http_skip_tls_verify": 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: <msgpack|json>
"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
"default_timezone": "Local", // default timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB>
"connect_attempts": 3, // initial server connect attempts
"response_cache_ttl": "3s", // the life span of a cached response
"reconnects": -1, // number of retries in case of connection lost
"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": {
"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>
},
"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
},
"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", "cdr_field_id": "cgrid", "type": "cdrfield", "value": "cgrid"},
{"tag":"RunId", "cdr_field_id": "mediation_runid", "type": "cdrfield", "value": "mediation_runid"},
{"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": "setup_time", "type": "cdrfield", "value": "setup_time", "layout": "2006-01-02T15:04:05Z07:00"},
{"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": [], // 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": "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},
],
"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_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": false, // starts Aliases service: <true|false>.
},
"users": {
"enabled": false, // 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
},
}`

294
config/config_json.go Normal file
View File

@@ -0,0 +1,294 @@
/*
Real-time Charging System 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 config
import (
"encoding/json"
"io"
"os"
"github.com/DisposaBoy/JsonConfigReader"
)
const (
GENERAL_JSN = "general"
LISTEN_JSN = "listen"
TPDB_JSN = "tariffplan_db"
DATADB_JSN = "data_db"
STORDB_JSN = "stor_db"
BALANCER_JSN = "balancer"
RATER_JSN = "rater"
SCHEDULER_JSN = "scheduler"
CDRS_JSN = "cdrs"
MEDIATOR_JSN = "mediator"
CDRSTATS_JSN = "cdrstats"
CDRE_JSN = "cdre"
CDRC_JSN = "cdrc"
SMFS_JSN = "sm_freeswitch"
SMKAM_JSN = "sm_kamailio"
SMOSIPS_JSN = "sm_opensips"
SM_JSN = "session_manager"
FS_JSN = "freeswitch"
KAMAILIO_JSN = "kamailio"
OSIPS_JSN = "opensips"
HISTSERV_JSN = "historys"
PUBSUBSERV_JSN = "pubsubs"
ALIASESSERV_JSN = "aliases"
USERSERV_JSN = "users"
MAILER_JSN = "mailer"
)
// Loads the json config out of io.Reader, eg other sources than file, maybe over http
func NewCgrJsonCfgFromReader(r io.Reader) (*CgrJsonCfg, error) {
var cgrJsonCfg CgrJsonCfg
jr := JsonConfigReader.New(r)
if err := json.NewDecoder(jr).Decode(&cgrJsonCfg); err != nil {
return nil, err
}
return &cgrJsonCfg, nil
}
// Loads the config out of file
func NewCgrJsonCfgFromFile(fpath string) (*CgrJsonCfg, error) {
cfgFile, err := os.Open(fpath)
if err != nil {
return nil, err
}
defer cfgFile.Close()
return NewCgrJsonCfgFromReader(cfgFile)
}
// Main object holding the loaded config as section raw messages
type CgrJsonCfg map[string]*json.RawMessage
func (self CgrJsonCfg) GeneralJsonCfg() (*GeneralJsonCfg, error) {
rawCfg, hasKey := self[GENERAL_JSN]
if !hasKey {
return nil, nil
}
cfg := new(GeneralJsonCfg)
if err := json.Unmarshal(*rawCfg, cfg); err != nil {
return nil, err
}
return cfg, nil
}
func (self CgrJsonCfg) ListenJsonCfg() (*ListenJsonCfg, error) {
rawCfg, hasKey := self[LISTEN_JSN]
if !hasKey {
return nil, nil
}
cfg := new(ListenJsonCfg)
if err := json.Unmarshal(*rawCfg, cfg); err != nil {
return nil, err
}
return cfg, nil
}
func (self CgrJsonCfg) DbJsonCfg(section string) (*DbJsonCfg, error) {
rawCfg, hasKey := self[section]
if !hasKey {
return nil, nil
}
cfg := new(DbJsonCfg)
if err := json.Unmarshal(*rawCfg, cfg); err != nil {
return nil, err
}
return cfg, nil
}
func (self CgrJsonCfg) BalancerJsonCfg() (*BalancerJsonCfg, error) {
rawCfg, hasKey := self[BALANCER_JSN]
if !hasKey {
return nil, nil
}
cfg := new(BalancerJsonCfg)
if err := json.Unmarshal(*rawCfg, cfg); err != nil {
return nil, err
}
return cfg, nil
}
func (self CgrJsonCfg) RaterJsonCfg() (*RaterJsonCfg, error) {
rawCfg, hasKey := self[RATER_JSN]
if !hasKey {
return nil, nil
}
cfg := new(RaterJsonCfg)
if err := json.Unmarshal(*rawCfg, cfg); err != nil {
return nil, err
}
return cfg, nil
}
func (self CgrJsonCfg) SchedulerJsonCfg() (*SchedulerJsonCfg, error) {
rawCfg, hasKey := self[SCHEDULER_JSN]
if !hasKey {
return nil, nil
}
cfg := new(SchedulerJsonCfg)
if err := json.Unmarshal(*rawCfg, cfg); err != nil {
return nil, err
}
return cfg, nil
}
func (self CgrJsonCfg) CdrsJsonCfg() (*CdrsJsonCfg, error) {
rawCfg, hasKey := self[CDRS_JSN]
if !hasKey {
return nil, nil
}
cfg := new(CdrsJsonCfg)
if err := json.Unmarshal(*rawCfg, cfg); err != nil {
return nil, err
}
return cfg, nil
}
func (self CgrJsonCfg) CdrStatsJsonCfg() (*CdrStatsJsonCfg, error) {
rawCfg, hasKey := self[CDRSTATS_JSN]
if !hasKey {
return nil, nil
}
cfg := new(CdrStatsJsonCfg)
if err := json.Unmarshal(*rawCfg, cfg); err != nil {
return nil, err
}
return cfg, nil
}
func (self CgrJsonCfg) CdreJsonCfgs() (map[string]*CdreJsonCfg, error) {
rawCfg, hasKey := self[CDRE_JSN]
if !hasKey {
return nil, nil
}
cfg := make(map[string]*CdreJsonCfg)
if err := json.Unmarshal(*rawCfg, &cfg); err != nil {
return nil, err
}
return cfg, nil
}
func (self CgrJsonCfg) CdrcJsonCfg() (map[string]*CdrcJsonCfg, error) {
rawCfg, hasKey := self[CDRC_JSN]
if !hasKey {
return nil, nil
}
cfg := make(map[string]*CdrcJsonCfg)
if err := json.Unmarshal(*rawCfg, &cfg); err != nil {
return nil, err
}
return cfg, nil
}
func (self CgrJsonCfg) SmFsJsonCfg() (*SmFsJsonCfg, error) {
rawCfg, hasKey := self[SMFS_JSN]
if !hasKey {
return nil, nil
}
cfg := new(SmFsJsonCfg)
if err := json.Unmarshal(*rawCfg, cfg); err != nil {
return nil, err
}
return cfg, nil
}
func (self CgrJsonCfg) SmKamJsonCfg() (*SmKamJsonCfg, error) {
rawCfg, hasKey := self[SMKAM_JSN]
if !hasKey {
return nil, nil
}
cfg := new(SmKamJsonCfg)
if err := json.Unmarshal(*rawCfg, cfg); err != nil {
return nil, err
}
return cfg, nil
}
func (self CgrJsonCfg) SmOsipsJsonCfg() (*SmOsipsJsonCfg, error) {
rawCfg, hasKey := self[SMOSIPS_JSN]
if !hasKey {
return nil, nil
}
cfg := new(SmOsipsJsonCfg)
if err := json.Unmarshal(*rawCfg, cfg); err != nil {
return nil, err
}
return cfg, nil
}
func (self CgrJsonCfg) HistServJsonCfg() (*HistServJsonCfg, error) {
rawCfg, hasKey := self[HISTSERV_JSN]
if !hasKey {
return nil, nil
}
cfg := new(HistServJsonCfg)
if err := json.Unmarshal(*rawCfg, cfg); err != nil {
return nil, err
}
return cfg, nil
}
func (self CgrJsonCfg) PubSubServJsonCfg() (*PubSubServJsonCfg, error) {
rawCfg, hasKey := self[PUBSUBSERV_JSN]
if !hasKey {
return nil, nil
}
cfg := new(PubSubServJsonCfg)
if err := json.Unmarshal(*rawCfg, cfg); err != nil {
return nil, err
}
return cfg, nil
}
func (self CgrJsonCfg) AliasesServJsonCfg() (*AliasesServJsonCfg, error) {
rawCfg, hasKey := self[ALIASESSERV_JSN]
if !hasKey {
return nil, nil
}
cfg := new(AliasesServJsonCfg)
if err := json.Unmarshal(*rawCfg, cfg); err != nil {
return nil, err
}
return cfg, nil
}
func (self CgrJsonCfg) UserServJsonCfg() (*UserServJsonCfg, error) {
rawCfg, hasKey := self[USERSERV_JSN]
if !hasKey {
return nil, nil
}
cfg := new(UserServJsonCfg)
if err := json.Unmarshal(*rawCfg, cfg); err != nil {
return nil, err
}
return cfg, nil
}
func (self CgrJsonCfg) MailerJsonCfg() (*MailerJsonCfg, error) {
rawCfg, hasKey := self[MAILER_JSN]
if !hasKey {
return nil, nil
}
cfg := new(MailerJsonCfg)
if err := json.Unmarshal(*rawCfg, cfg); err != nil {
return nil, err
}
return cfg, nil
}

518
config/config_json_test.go Normal file
View File

@@ -0,0 +1,518 @@
/*
Real-time Charging System 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 config
import (
"reflect"
"strings"
"testing"
"github.com/cgrates/cgrates/utils"
)
var dfCgrJsonCfg *CgrJsonCfg
// Loads up the default configuration and tests it's sections one by one
func TestDfNewdfCgrJsonCfgFromReader(t *testing.T) {
var err error
if dfCgrJsonCfg, err = NewCgrJsonCfgFromReader(strings.NewReader(CGRATES_CFG_JSON)); err != nil {
t.Error(err)
}
}
func TestDfGeneralJsonCfg(t *testing.T) {
eCfg := &GeneralJsonCfg{
Http_skip_tls_verify: utils.BoolPointer(false),
Rounding_decimals: utils.IntPointer(10),
Dbdata_encoding: utils.StringPointer("msgpack"),
Tpexport_dir: utils.StringPointer("/var/log/cgrates/tpe"),
Default_reqtype: 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"),
Internal_ttl: utils.StringPointer("2m")}
if gCfg, err := dfCgrJsonCfg.GeneralJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, gCfg) {
t.Error("Received: ", gCfg)
}
}
func TestDfListenJsonCfg(t *testing.T) {
eCfg := &ListenJsonCfg{
Rpc_json: utils.StringPointer("127.0.0.1:2012"),
Rpc_gob: utils.StringPointer("127.0.0.1:2013"),
Http: utils.StringPointer("127.0.0.1:2080")}
if cfg, err := dfCgrJsonCfg.ListenJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Error("Received: ", cfg)
}
}
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(""),
}
if cfg, err := dfCgrJsonCfg.DbJsonCfg(TPDB_JSN); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Error("Received: ", cfg)
}
eCfg = &DbJsonCfg{
Db_type: utils.StringPointer("redis"),
Db_host: utils.StringPointer("127.0.0.1"),
Db_port: utils.IntPointer(6379),
Db_name: utils.StringPointer("11"),
Db_user: utils.StringPointer(""),
Db_passwd: utils.StringPointer(""),
Load_history_size: utils.IntPointer(10),
}
if cfg, err := dfCgrJsonCfg.DbJsonCfg(DATADB_JSN); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Error("Received: ", cfg)
}
eCfg = &DbJsonCfg{
Db_type: utils.StringPointer("mysql"),
Db_host: utils.StringPointer("127.0.0.1"),
Db_port: utils.IntPointer(3306),
Db_name: utils.StringPointer("cgrates"),
Db_user: utils.StringPointer("cgrates"),
Db_passwd: utils.StringPointer("CGRateS.org"),
Max_open_conns: utils.IntPointer(100),
Max_idle_conns: utils.IntPointer(10),
}
if cfg, err := dfCgrJsonCfg.DbJsonCfg(STORDB_JSN); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Error("Received: ", cfg)
}
}
func TestDfBalancerJsonCfg(t *testing.T) {
eCfg := &BalancerJsonCfg{Enabled: utils.BoolPointer(false)}
if cfg, err := dfCgrJsonCfg.BalancerJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Error("Received: ", cfg)
}
}
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("")}
if cfg, err := dfCgrJsonCfg.RaterJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Errorf("Received: %+v", cfg)
}
}
func TestDfSchedulerJsonCfg(t *testing.T) {
eCfg := &SchedulerJsonCfg{Enabled: utils.BoolPointer(false)}
if cfg, err := dfCgrJsonCfg.SchedulerJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Error("Received: ", cfg)
}
}
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(""),
Cdr_replication: &[]*CdrReplicationJsonCfg{},
}
if cfg, err := dfCgrJsonCfg.CdrsJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Errorf("Received: %+v", *cfg)
}
}
func TestDfCdrStatsJsonCfg(t *testing.T) {
eCfg := &CdrStatsJsonCfg{
Enabled: utils.BoolPointer(false),
Save_Interval: utils.StringPointer("1m"),
}
if cfg, err := dfCgrJsonCfg.CdrStatsJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Error("Received: ", *cfg)
}
}
func TestDfCdreJsonCfgs(t *testing.T) {
eFields := []*CdrFieldJsonCfg{}
eContentFlds := []*CdrFieldJsonCfg{
&CdrFieldJsonCfg{Tag: utils.StringPointer("CgrId"),
Cdr_field_id: utils.StringPointer("cgrid"),
Type: utils.StringPointer("cdrfield"),
Value: utils.StringPointer("cgrid")},
&CdrFieldJsonCfg{Tag: utils.StringPointer("RunId"),
Cdr_field_id: utils.StringPointer("mediation_runid"),
Type: utils.StringPointer("cdrfield"),
Value: utils.StringPointer("mediation_runid")},
&CdrFieldJsonCfg{Tag: utils.StringPointer("Tor"),
Cdr_field_id: utils.StringPointer("tor"),
Type: utils.StringPointer("cdrfield"),
Value: utils.StringPointer("tor")},
&CdrFieldJsonCfg{Tag: utils.StringPointer("AccId"),
Cdr_field_id: utils.StringPointer("accid"),
Type: utils.StringPointer("cdrfield"),
Value: utils.StringPointer("accid")},
&CdrFieldJsonCfg{Tag: utils.StringPointer("ReqType"),
Cdr_field_id: utils.StringPointer("reqtype"),
Type: utils.StringPointer("cdrfield"),
Value: utils.StringPointer("reqtype")},
&CdrFieldJsonCfg{Tag: utils.StringPointer("Direction"),
Cdr_field_id: utils.StringPointer("direction"),
Type: utils.StringPointer("cdrfield"),
Value: utils.StringPointer("direction")},
&CdrFieldJsonCfg{Tag: utils.StringPointer("Tenant"),
Cdr_field_id: utils.StringPointer("tenant"),
Type: utils.StringPointer("cdrfield"),
Value: utils.StringPointer("tenant")},
&CdrFieldJsonCfg{Tag: utils.StringPointer("Category"),
Cdr_field_id: utils.StringPointer("category"),
Type: utils.StringPointer("cdrfield"),
Value: utils.StringPointer("category")},
&CdrFieldJsonCfg{Tag: utils.StringPointer("Account"),
Cdr_field_id: utils.StringPointer("account"),
Type: utils.StringPointer("cdrfield"),
Value: utils.StringPointer("account")},
&CdrFieldJsonCfg{Tag: utils.StringPointer("Subject"),
Cdr_field_id: utils.StringPointer("subject"),
Type: utils.StringPointer("cdrfield"),
Value: utils.StringPointer("subject")},
&CdrFieldJsonCfg{Tag: utils.StringPointer("Destination"),
Cdr_field_id: utils.StringPointer("destination"),
Type: utils.StringPointer("cdrfield"),
Value: utils.StringPointer("destination")},
&CdrFieldJsonCfg{Tag: utils.StringPointer("SetupTime"),
Cdr_field_id: utils.StringPointer("setup_time"),
Type: utils.StringPointer("cdrfield"),
Value: utils.StringPointer("setup_time"),
Layout: utils.StringPointer("2006-01-02T15:04:05Z07:00")},
&CdrFieldJsonCfg{Tag: utils.StringPointer("AnswerTime"),
Cdr_field_id: utils.StringPointer("answer_time"),
Type: utils.StringPointer("cdrfield"),
Value: utils.StringPointer("answer_time"),
Layout: utils.StringPointer("2006-01-02T15:04:05Z07:00")},
&CdrFieldJsonCfg{Tag: utils.StringPointer("Usage"),
Cdr_field_id: utils.StringPointer("usage"),
Type: utils.StringPointer("cdrfield"),
Value: utils.StringPointer("usage")},
&CdrFieldJsonCfg{Tag: utils.StringPointer("Cost"),
Cdr_field_id: utils.StringPointer("cost"),
Type: utils.StringPointer("cdrfield"),
Value: utils.StringPointer("cost")},
}
eCfg := map[string]*CdreJsonCfg{
utils.META_DEFAULT: &CdreJsonCfg{
Cdr_format: utils.StringPointer("csv"),
Field_separator: utils.StringPointer(","),
Data_usage_multiply_factor: utils.Float64Pointer(1.0),
Sms_usage_multiply_factor: utils.Float64Pointer(1.0),
Generic_usage_multiply_factor: utils.Float64Pointer(1.0),
Cost_multiply_factor: utils.Float64Pointer(1.0),
Cost_rounding_decimals: utils.IntPointer(-1),
Cost_shift_digits: utils.IntPointer(0),
Mask_destination_id: utils.StringPointer("MASKED_DESTINATIONS"),
Mask_length: utils.IntPointer(0),
Export_dir: utils.StringPointer("/var/log/cgrates/cdre"),
Header_fields: &eFields,
Content_fields: &eContentFlds,
Trailer_fields: &eFields,
},
}
if cfg, err := dfCgrJsonCfg.CdreJsonCfgs(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Error("Received: ", cfg)
}
}
func TestDfCdrcJsonCfg(t *testing.T) {
eFields := []*CdrFieldJsonCfg{}
cdrFields := []*CdrFieldJsonCfg{
&CdrFieldJsonCfg{Tag: utils.StringPointer("tor"), Cdr_field_id: utils.StringPointer("tor"), Type: utils.StringPointer(utils.CDRFIELD),
Value: utils.StringPointer("2"), Mandatory: utils.BoolPointer(true)},
&CdrFieldJsonCfg{Tag: utils.StringPointer("accid"), Cdr_field_id: utils.StringPointer("accid"), Type: utils.StringPointer(utils.CDRFIELD),
Value: utils.StringPointer("3"), Mandatory: utils.BoolPointer(true)},
&CdrFieldJsonCfg{Tag: utils.StringPointer("reqtype"), Cdr_field_id: utils.StringPointer("reqtype"), Type: utils.StringPointer(utils.CDRFIELD),
Value: utils.StringPointer("4"), Mandatory: utils.BoolPointer(true)},
&CdrFieldJsonCfg{Tag: utils.StringPointer("direction"), Cdr_field_id: utils.StringPointer("direction"), Type: utils.StringPointer(utils.CDRFIELD),
Value: utils.StringPointer("5"), Mandatory: utils.BoolPointer(true)},
&CdrFieldJsonCfg{Tag: utils.StringPointer("tenant"), Cdr_field_id: utils.StringPointer("tenant"), Type: utils.StringPointer(utils.CDRFIELD),
Value: utils.StringPointer("6"), Mandatory: utils.BoolPointer(true)},
&CdrFieldJsonCfg{Tag: utils.StringPointer("category"), Cdr_field_id: utils.StringPointer("category"), Type: utils.StringPointer(utils.CDRFIELD),
Value: utils.StringPointer("7"), Mandatory: utils.BoolPointer(true)},
&CdrFieldJsonCfg{Tag: utils.StringPointer("account"), Cdr_field_id: utils.StringPointer("account"), Type: utils.StringPointer(utils.CDRFIELD),
Value: utils.StringPointer("8"), Mandatory: utils.BoolPointer(true)},
&CdrFieldJsonCfg{Tag: utils.StringPointer("subject"), Cdr_field_id: utils.StringPointer("subject"), Type: utils.StringPointer(utils.CDRFIELD),
Value: utils.StringPointer("9"), Mandatory: utils.BoolPointer(true)},
&CdrFieldJsonCfg{Tag: utils.StringPointer("destination"), Cdr_field_id: utils.StringPointer("destination"), Type: utils.StringPointer(utils.CDRFIELD),
Value: utils.StringPointer("10"), Mandatory: utils.BoolPointer(true)},
&CdrFieldJsonCfg{Tag: utils.StringPointer("setup_time"), Cdr_field_id: utils.StringPointer("setup_time"), Type: utils.StringPointer(utils.CDRFIELD),
Value: utils.StringPointer("11"), Mandatory: utils.BoolPointer(true)},
&CdrFieldJsonCfg{Tag: utils.StringPointer("answer_time"), Cdr_field_id: utils.StringPointer("answer_time"), Type: utils.StringPointer(utils.CDRFIELD),
Value: utils.StringPointer("12"), Mandatory: utils.BoolPointer(true)},
&CdrFieldJsonCfg{Tag: utils.StringPointer("usage"), Cdr_field_id: utils.StringPointer("usage"), Type: utils.StringPointer(utils.CDRFIELD),
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"),
Cdr_format: utils.StringPointer("csv"),
Field_separator: utils.StringPointer(","),
Timezone: utils.StringPointer(""),
Run_delay: utils.IntPointer(0),
Max_open_files: utils.IntPointer(1024),
Data_usage_multiply_factor: utils.Float64Pointer(1024.0),
Cdr_in_dir: utils.StringPointer("/var/log/cgrates/cdrc/in"),
Cdr_out_dir: utils.StringPointer("/var/log/cgrates/cdrc/out"),
Failed_calls_prefix: utils.StringPointer("missed_calls"),
Cdr_source_id: utils.StringPointer("freeswitch_csv"),
Cdr_filter: utils.StringPointer(""),
Partial_record_cache: utils.StringPointer("10s"),
Header_fields: &eFields,
Content_fields: &cdrFields,
Trailer_fields: &eFields,
},
}
if cfg, err := dfCgrJsonCfg.CdrcJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Error("Received: ", cfg["*default"])
}
}
func TestSmFsJsonCfg(t *testing.T) {
eCfg := &SmFsJsonCfg{
Enabled: utils.BoolPointer(false),
Rater: utils.StringPointer("internal"),
Cdrs: utils.StringPointer("internal"),
Create_cdr: utils.BoolPointer(false),
Extra_fields: utils.StringSlicePointer([]string{}),
Debit_interval: utils.StringPointer("10s"),
Min_call_duration: utils.StringPointer("0s"),
Max_call_duration: utils.StringPointer("3h"),
Min_dur_low_balance: utils.StringPointer("5s"),
Low_balance_ann_file: utils.StringPointer(""),
Empty_balance_context: utils.StringPointer(""),
Empty_balance_ann_file: utils.StringPointer(""),
Subscribe_park: utils.BoolPointer(true),
Channel_sync_interval: utils.StringPointer("5m"),
Connections: &[]*FsConnJsonCfg{
&FsConnJsonCfg{
Server: utils.StringPointer("127.0.0.1:8021"),
Password: utils.StringPointer("ClueCon"),
Reconnects: utils.IntPointer(5),
}},
}
if cfg, err := dfCgrJsonCfg.SmFsJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Error("Received: ", cfg)
}
}
func TestSmKamJsonCfg(t *testing.T) {
eCfg := &SmKamJsonCfg{
Enabled: utils.BoolPointer(false),
Rater: utils.StringPointer("internal"),
Cdrs: utils.StringPointer("internal"),
Create_cdr: utils.BoolPointer(false),
Debit_interval: utils.StringPointer("10s"),
Min_call_duration: utils.StringPointer("0s"),
Max_call_duration: utils.StringPointer("3h"),
Connections: &[]*KamConnJsonCfg{
&KamConnJsonCfg{
Evapi_addr: utils.StringPointer("127.0.0.1:8448"),
Reconnects: utils.IntPointer(5),
},
},
}
if cfg, err := dfCgrJsonCfg.SmKamJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Error("Received: ", cfg)
}
}
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"),
Create_cdr: utils.BoolPointer(false),
Debit_interval: utils.StringPointer("10s"),
Min_call_duration: utils.StringPointer("0s"),
Max_call_duration: utils.StringPointer("3h"),
Events_subscribe_interval: utils.StringPointer("60s"),
Mi_addr: utils.StringPointer("127.0.0.1:8020"),
}
if cfg, err := dfCgrJsonCfg.SmOsipsJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Error("Received: ", cfg)
}
}
func TestDfHistServJsonCfg(t *testing.T) {
eCfg := &HistServJsonCfg{
Enabled: utils.BoolPointer(false),
History_dir: utils.StringPointer("/var/log/cgrates/history"),
Save_interval: utils.StringPointer("1s"),
}
if cfg, err := dfCgrJsonCfg.HistServJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Error("Received: ", cfg)
}
}
func TestDfPubSubServJsonCfg(t *testing.T) {
eCfg := &PubSubServJsonCfg{
Enabled: utils.BoolPointer(false),
}
if cfg, err := dfCgrJsonCfg.PubSubServJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Error("Received: ", cfg)
}
}
func TestDfAliasesServJsonCfg(t *testing.T) {
eCfg := &AliasesServJsonCfg{
Enabled: utils.BoolPointer(false),
}
if cfg, err := dfCgrJsonCfg.AliasesServJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Error("Received: ", cfg)
}
}
func TestDfUserServJsonCfg(t *testing.T) {
eCfg := &UserServJsonCfg{
Enabled: utils.BoolPointer(false),
Indexes: utils.StringSlicePointer([]string{}),
}
if cfg, err := dfCgrJsonCfg.UserServJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Error("Received: ", cfg)
}
}
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"),
}
if cfg, err := dfCgrJsonCfg.MailerJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfg, cfg) {
t.Error("Received: ", cfg)
}
}
func TestNewCgrJsonCfgFromFile(t *testing.T) {
cgrJsonCfg, err := NewCgrJsonCfgFromFile("cfg_data.json")
if err != nil {
t.Error(err)
}
eCfg := &GeneralJsonCfg{Default_reqtype: 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)
}
cdrFields := []*CdrFieldJsonCfg{
&CdrFieldJsonCfg{Cdr_field_id: utils.StringPointer("tor"), Value: utils.StringPointer("~7:s/^(voice|data|sms|generic)$/*$1/")},
&CdrFieldJsonCfg{Cdr_field_id: utils.StringPointer("answer_time"), Value: utils.StringPointer("1")},
&CdrFieldJsonCfg{Cdr_field_id: utils.StringPointer("usage"), Value: utils.StringPointer(`~9:s/^(\d+)$/${1}s/`)},
}
eCfgCdrc := map[string]*CdrcJsonCfg{
"CDRC-CSV1": &CdrcJsonCfg{
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{
Enabled: utils.BoolPointer(true),
Data_usage_multiply_factor: utils.Float64Pointer(0.000976563),
Run_delay: utils.IntPointer(1),
Cdr_in_dir: utils.StringPointer("/tmp/cgrates/cdrc2/in"),
Cdr_out_dir: utils.StringPointer("/tmp/cgrates/cdrc2/out"),
Cdr_source_id: utils.StringPointer("csv2"),
Content_fields: &cdrFields,
},
}
if cfg, err := cgrJsonCfg.CdrcJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfgCdrc, cfg) {
t.Error("Received: ", cfg["CDRC-CSV2"])
}
eCfgSmFs := &SmFsJsonCfg{
Enabled: utils.BoolPointer(true),
Connections: &[]*FsConnJsonCfg{
&FsConnJsonCfg{
Server: utils.StringPointer("1.2.3.4:8021"),
Password: utils.StringPointer("ClueCon"),
Reconnects: utils.IntPointer(5),
},
&FsConnJsonCfg{
Server: utils.StringPointer("2.3.4.5:8021"),
Password: utils.StringPointer("ClueCon"),
Reconnects: utils.IntPointer(5),
},
},
}
if smFsCfg, err := cgrJsonCfg.SmFsJsonCfg(); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCfgSmFs, smFsCfg) {
t.Error("Received: ", smFsCfg)
}
}

View File

@@ -1,6 +1,6 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2013 ITsysCOM
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
@@ -19,117 +19,42 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package config
import (
"fmt"
"reflect"
"testing"
)
func TestConfig(t *testing.T) {
cfgPth := "test_data.txt"
cfg, err := NewCGRConfig(&cfgPth)
if err != nil {
t.Log(fmt.Sprintf("Could not parse config: %s!", err))
t.FailNow()
var cfg *CGRConfig
func TestConfigSharing(t *testing.T) {
cfg, _ = NewDefaultCGRConfig()
SetCgrConfig(cfg)
cfgReturn := CgrConfig()
if !reflect.DeepEqual(cfgReturn, cfg) {
t.Errorf("Retrieved %v, Expected %v", cfgReturn, cfg)
}
if cfg.DataDBType != "test" ||
cfg.DataDBHost != "test" ||
cfg.DataDBPort != "test" ||
cfg.DataDBName != "test" ||
cfg.DataDBUser != "test" ||
cfg.DataDBPass != "test" ||
cfg.LogDBType != "test" ||
cfg.LogDBHost != "test" ||
cfg.LogDBPort != "test" ||
cfg.LogDBName != "test" ||
cfg.LogDBUser != "test" ||
cfg.LogDBPass != "test" ||
cfg.RaterEnabled != true ||
cfg.RaterBalancer != "test" ||
cfg.RaterListen != "test" ||
cfg.RaterRPCEncoding != "test" ||
cfg.BalancerEnabled != true ||
cfg.BalancerListen != "test" ||
cfg.BalancerRPCEncoding != "test" ||
cfg.SchedulerEnabled != true ||
cfg.SMEnabled != true ||
cfg.SMSwitchType != "test" ||
cfg.SMRater != "test" ||
cfg.SMDebitInterval != 11 ||
cfg.SMRPCEncoding != "test" ||
cfg.MediatorEnabled != true ||
cfg.MediatorCDRInDir != "test" ||
cfg.MediatorCDROutDir != "test" ||
cfg.MediatorRater != "test" ||
cfg.MediatorRPCEncoding != "test" ||
cfg.MediatorSkipDB != true ||
cfg.MediatorPseudoprepaid != true ||
cfg.FreeswitchServer != "test" ||
cfg.FreeswitchPass != "test" ||
cfg.FreeswitchDirectionIdx != "test" ||
cfg.FreeswitchTORIdx != "test" ||
cfg.FreeswitchTenantIdx != "test" ||
cfg.FreeswitchSubjectIdx != "test" ||
cfg.FreeswitchAccountIdx != "test" ||
cfg.FreeswitchDestIdx != "test" ||
cfg.FreeswitchTimeStartIdx != "test" ||
cfg.FreeswitchDurationIdx != "test" ||
cfg.FreeswitchUUIDIdx != "test" {
t.Log(cfg.DataDBType)
t.Log(cfg.DataDBHost)
t.Log(cfg.DataDBPort)
t.Log(cfg.DataDBName)
t.Log(cfg.DataDBUser)
t.Log(cfg.DataDBPass)
t.Log(cfg.LogDBType)
t.Log(cfg.LogDBHost)
t.Log(cfg.LogDBPort)
t.Log(cfg.LogDBName)
t.Log(cfg.LogDBUser)
t.Log(cfg.LogDBPass)
t.Log(cfg.RaterEnabled)
t.Log(cfg.RaterBalancer)
t.Log(cfg.RaterListen)
t.Log(cfg.RaterRPCEncoding)
t.Log(cfg.BalancerEnabled)
t.Log(cfg.BalancerListen)
t.Log(cfg.BalancerRPCEncoding)
t.Log(cfg.SchedulerEnabled)
t.Log(cfg.SMEnabled)
t.Log(cfg.SMSwitchType)
t.Log(cfg.SMRater)
t.Log(cfg.SMDebitInterval)
t.Log(cfg.SMRPCEncoding)
t.Log(cfg.MediatorEnabled)
t.Log(cfg.MediatorCDRInDir)
t.Log(cfg.MediatorCDROutDir)
t.Log(cfg.MediatorRater)
t.Log(cfg.MediatorRPCEncoding)
t.Log(cfg.MediatorSkipDB)
t.Log(cfg.MediatorPseudoprepaid)
t.Log(cfg.FreeswitchServer)
t.Log(cfg.FreeswitchPass)
t.Log(cfg.FreeswitchDirectionIdx)
t.Log(cfg.FreeswitchTORIdx)
t.Log(cfg.FreeswitchTenantIdx)
t.Log(cfg.FreeswitchSubjectIdx)
t.Log(cfg.FreeswitchAccountIdx)
t.Log(cfg.FreeswitchDestIdx)
t.Log(cfg.FreeswitchTimeStartIdx)
t.Log(cfg.FreeswitchDurationIdx)
t.Log(cfg.FreeswitchUUIDIdx)
t.Error("Config file read failed!")
}
}
func TestParamOverwrite(t *testing.T) {
cfgPth := "test_data.txt"
cfg, err := NewCGRConfig(&cfgPth)
if err != nil {
t.Log(fmt.Sprintf("Could not parse config: %s!", err))
t.FailNow()
func TestLoadCgrCfgWithDefaults(t *testing.T) {
JSN_CFG := `
{
"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}
],
},
}`
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},
}
if cfg.FreeswitchReconnects != 5 { // one default which is not overwritten in test data
t.Errorf("FreeswitchReconnects set == %d, expect 5", cfg.FreeswitchReconnects)
} else if cfg.SchedulerEnabled != true { // one parameter which should be overwritten in test data
t.Errorf("scheduler_enabled set == %d, expect true", cfg.SchedulerEnabled)
if cgrCfg, err := NewCGRConfigFromJsonStringWithDefaults(JSN_CFG); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eCgrCfg.SmFsConfig, cgrCfg.SmFsConfig) {
t.Errorf("Expected: %+v, received: %+v", eCgrCfg.SmFsConfig, cgrCfg.SmFsConfig)
}
}

194
config/configcdrc_test.go Normal file
View File

@@ -0,0 +1,194 @@
/*
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 config
import (
"reflect"
"testing"
"time"
"github.com/cgrates/cgrates/utils"
)
func TestLoadCdrcConfigMultipleFiles(t *testing.T) {
cgrCfg, err := NewCGRConfigFromFolder(".")
if err != nil {
t.Error(err)
}
eCgrCfg, _ := NewDefaultCGRConfig()
eCgrCfg.CdrcProfiles = make(map[string]map[string]*CdrcConfig)
// Default instance first
eCgrCfg.CdrcProfiles["/var/log/cgrates/cdrc/in"] = map[string]*CdrcConfig{
"*default": &CdrcConfig{
Enabled: false,
Cdrs: "internal",
CdrFormat: "csv",
FieldSeparator: ',',
DataUsageMultiplyFactor: 1024,
RunDelay: 0,
MaxOpenFiles: 1024,
CdrInDir: "/var/log/cgrates/cdrc/in",
CdrOutDir: "/var/log/cgrates/cdrc/out",
FailedCallsPrefix: "missed_calls",
CdrSourceId: "freeswitch_csv",
CdrFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP),
PartialRecordCache: time.Duration(10) * time.Second,
HeaderFields: make([]*CfgCdrField, 0),
ContentFields: []*CfgCdrField{
&CfgCdrField{Tag: "tor", Type: "cdrfield", CdrFieldId: "tor", Value: utils.ParseRSRFieldsMustCompile("2", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "accid", Type: "cdrfield", CdrFieldId: "accid", Value: utils.ParseRSRFieldsMustCompile("3", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "reqtype", Type: "cdrfield", CdrFieldId: "reqtype", Value: utils.ParseRSRFieldsMustCompile("4", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "direction", Type: "cdrfield", CdrFieldId: "direction", Value: utils.ParseRSRFieldsMustCompile("5", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "tenant", Type: "cdrfield", CdrFieldId: "tenant", Value: utils.ParseRSRFieldsMustCompile("6", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "category", Type: "cdrfield", CdrFieldId: "category", Value: utils.ParseRSRFieldsMustCompile("7", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "account", Type: "cdrfield", CdrFieldId: "account", Value: utils.ParseRSRFieldsMustCompile("8", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "subject", Type: "cdrfield", CdrFieldId: "subject", Value: utils.ParseRSRFieldsMustCompile("9", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "destination", Type: "cdrfield", CdrFieldId: "destination", Value: utils.ParseRSRFieldsMustCompile("10", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "setup_time", Type: "cdrfield", CdrFieldId: "setup_time", Value: utils.ParseRSRFieldsMustCompile("11", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "answer_time", Type: "cdrfield", CdrFieldId: "answer_time", Value: utils.ParseRSRFieldsMustCompile("12", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "usage", Type: "cdrfield", CdrFieldId: "usage", Value: utils.ParseRSRFieldsMustCompile("13", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
},
TrailerFields: make([]*CfgCdrField, 0),
},
}
eCgrCfg.CdrcProfiles["/tmp/cgrates/cdrc1/in"] = map[string]*CdrcConfig{
"CDRC-CSV1": &CdrcConfig{
Enabled: true,
Cdrs: "internal",
CdrFormat: "csv",
FieldSeparator: ',',
DataUsageMultiplyFactor: 1024,
RunDelay: 0,
MaxOpenFiles: 1024,
CdrInDir: "/tmp/cgrates/cdrc1/in",
CdrOutDir: "/tmp/cgrates/cdrc1/out",
CdrSourceId: "csv1",
CdrFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP),
HeaderFields: make([]*CfgCdrField, 0),
ContentFields: []*CfgCdrField{
&CfgCdrField{Tag: "tor", Type: "cdrfield", CdrFieldId: "tor", Value: utils.ParseRSRFieldsMustCompile("2", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "accid", Type: "cdrfield", CdrFieldId: "accid", Value: utils.ParseRSRFieldsMustCompile("3", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "reqtype", Type: "cdrfield", CdrFieldId: "reqtype", Value: utils.ParseRSRFieldsMustCompile("4", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "direction", Type: "cdrfield", CdrFieldId: "direction", Value: utils.ParseRSRFieldsMustCompile("5", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "tenant", Type: "cdrfield", CdrFieldId: "tenant", Value: utils.ParseRSRFieldsMustCompile("6", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "category", Type: "cdrfield", CdrFieldId: "category", Value: utils.ParseRSRFieldsMustCompile("7", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "account", Type: "cdrfield", CdrFieldId: "account", Value: utils.ParseRSRFieldsMustCompile("8", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "subject", Type: "cdrfield", CdrFieldId: "subject", Value: utils.ParseRSRFieldsMustCompile("9", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "destination", Type: "cdrfield", CdrFieldId: "destination", Value: utils.ParseRSRFieldsMustCompile("10", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "setup_time", Type: "cdrfield", CdrFieldId: "setup_time", Value: utils.ParseRSRFieldsMustCompile("11", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "answer_time", Type: "cdrfield", CdrFieldId: "answer_time", Value: utils.ParseRSRFieldsMustCompile("12", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "usage", Type: "cdrfield", CdrFieldId: "usage", Value: utils.ParseRSRFieldsMustCompile("13", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
},
TrailerFields: make([]*CfgCdrField, 0),
},
}
eCgrCfg.CdrcProfiles["/tmp/cgrates/cdrc2/in"] = map[string]*CdrcConfig{
"CDRC-CSV2": &CdrcConfig{
Enabled: true,
Cdrs: "internal",
CdrFormat: "csv",
FieldSeparator: ',',
DataUsageMultiplyFactor: 0.000976563,
RunDelay: 0,
MaxOpenFiles: 1024,
CdrInDir: "/tmp/cgrates/cdrc2/in",
CdrOutDir: "/tmp/cgrates/cdrc2/out",
CdrSourceId: "csv2",
CdrFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP),
HeaderFields: make([]*CfgCdrField, 0),
ContentFields: []*CfgCdrField{
&CfgCdrField{Tag: "", Type: "", CdrFieldId: "tor", Value: utils.ParseRSRFieldsMustCompile("~7:s/^(voice|data|sms|generic)$/*$1/", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: false},
&CfgCdrField{Tag: "", Type: "", CdrFieldId: "answer_time", Value: utils.ParseRSRFieldsMustCompile("2", 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{
Enabled: true,
Cdrs: "internal",
CdrFormat: "csv",
FieldSeparator: ',',
DataUsageMultiplyFactor: 1024,
RunDelay: 0,
MaxOpenFiles: 1024,
CdrInDir: "/tmp/cgrates/cdrc3/in",
CdrOutDir: "/tmp/cgrates/cdrc3/out",
CdrSourceId: "csv3",
CdrFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP),
HeaderFields: make([]*CfgCdrField, 0),
ContentFields: []*CfgCdrField{
&CfgCdrField{Tag: "tor", Type: "cdrfield", CdrFieldId: "tor", Value: utils.ParseRSRFieldsMustCompile("2", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "accid", Type: "cdrfield", CdrFieldId: "accid", Value: utils.ParseRSRFieldsMustCompile("3", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "reqtype", Type: "cdrfield", CdrFieldId: "reqtype", Value: utils.ParseRSRFieldsMustCompile("4", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "direction", Type: "cdrfield", CdrFieldId: "direction", Value: utils.ParseRSRFieldsMustCompile("5", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "tenant", Type: "cdrfield", CdrFieldId: "tenant", Value: utils.ParseRSRFieldsMustCompile("6", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "category", Type: "cdrfield", CdrFieldId: "category", Value: utils.ParseRSRFieldsMustCompile("7", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "account", Type: "cdrfield", CdrFieldId: "account", Value: utils.ParseRSRFieldsMustCompile("8", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "subject", Type: "cdrfield", CdrFieldId: "subject", Value: utils.ParseRSRFieldsMustCompile("9", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "destination", Type: "cdrfield", CdrFieldId: "destination", Value: utils.ParseRSRFieldsMustCompile("10", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "setup_time", Type: "cdrfield", CdrFieldId: "setup_time", Value: utils.ParseRSRFieldsMustCompile("11", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "answer_time", Type: "cdrfield", CdrFieldId: "answer_time", Value: utils.ParseRSRFieldsMustCompile("12", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
&CfgCdrField{Tag: "usage", Type: "cdrfield", CdrFieldId: "usage", Value: utils.ParseRSRFieldsMustCompile("13", utils.INFIELD_SEP),
FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true},
},
TrailerFields: make([]*CfgCdrField, 0),
},
}
if !reflect.DeepEqual(eCgrCfg.CdrcProfiles, cgrCfg.CdrcProfiles) {
t.Errorf("Expected: %+v, received: %+v", eCgrCfg.CdrcProfiles, cgrCfg.CdrcProfiles)
}
}

30
config/libconfig.go Normal file
View File

@@ -0,0 +1,30 @@
/*
Real-time Charging System 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 config
import (
"github.com/cgrates/cgrates/utils"
)
type CdrReplicationCfg struct {
Transport string
Server string
Synchronous bool
CdrFilter utils.RSRFields // Only replicate if the filters here are matching
}

253
config/libconfig_json.go Normal file
View File

@@ -0,0 +1,253 @@
/*
Real-time Charging System 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 config
// General config section
type GeneralJsonCfg struct {
Http_skip_tls_verify *bool
Rounding_decimals *int
Dbdata_encoding *string
Tpexport_dir *string
Default_reqtype *string
Default_category *string
Default_tenant *string
Default_subject *string
Default_timezone *string
Reconnects *int
Connect_attempts *int
Response_cache_ttl *string
Internal_ttl *string
}
// Listen config section
type ListenJsonCfg struct {
Rpc_json *string
Rpc_gob *string
Http *string
}
// Database config
type DbJsonCfg struct {
Db_type *string
Db_host *string
Db_port *int
Db_name *string
Db_user *string
Db_passwd *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
}
// Balancer config section
type BalancerJsonCfg struct {
Enabled *bool
}
// Rater config section
type RaterJsonCfg struct {
Enabled *bool
Balancer *string
Cdrstats *string
Historys *string
Pubsubs *string
Aliases *string
Users *string
}
// Scheduler config section
type SchedulerJsonCfg struct {
Enabled *bool
}
// Cdrs config section
type CdrsJsonCfg struct {
Enabled *bool
Extra_fields *[]string
Store_cdrs *bool
Rater *string
Pubsubs *string
Users *string
Aliases *string
Cdrstats *string
Cdr_replication *[]*CdrReplicationJsonCfg
}
type CdrReplicationJsonCfg struct {
Transport *string
Server *string
Synchronous *bool
Cdr_filter *string
}
// Cdrstats config section
type CdrStatsJsonCfg struct {
Enabled *bool
Save_Interval *string
}
// One cdr field config, used in cdre and cdrc
type CdrFieldJsonCfg struct {
Tag *string
Type *string
Cdr_field_id *string
Metatag_id *string
Value *string
Width *int
Strip *string
Padding *string
Layout *string
Field_filter *string
Mandatory *bool
}
// Cdre config section
type CdreJsonCfg struct {
Cdr_format *string
Field_separator *string
Data_usage_multiply_factor *float64
Sms_usage_multiply_factor *float64
Generic_usage_multiply_factor *float64
Cost_multiply_factor *float64
Cost_rounding_decimals *int
Cost_shift_digits *int
Mask_destination_id *string
Mask_length *int
Export_dir *string
Header_fields *[]*CdrFieldJsonCfg
Content_fields *[]*CdrFieldJsonCfg
Trailer_fields *[]*CdrFieldJsonCfg
}
// Cdrc config section
type CdrcJsonCfg struct {
Enabled *bool
Dry_run *bool
Cdrs *string
Cdr_format *string
Field_separator *string
Timezone *string
Run_delay *int
Data_usage_multiply_factor *float64
Cdr_in_dir *string
Cdr_out_dir *string
Failed_calls_prefix *string
Cdr_source_id *string
Cdr_filter *string
Max_open_files *int
Partial_record_cache *string
Header_fields *[]*CdrFieldJsonCfg
Content_fields *[]*CdrFieldJsonCfg
Trailer_fields *[]*CdrFieldJsonCfg
}
// SM-FreeSWITCH config section
type SmFsJsonCfg struct {
Enabled *bool
Rater *string
Cdrs *string
Create_cdr *bool
Extra_fields *[]string
Debit_interval *string
Min_call_duration *string
Max_call_duration *string
Min_dur_low_balance *string
Low_balance_ann_file *string
Empty_balance_context *string
Empty_balance_ann_file *string
Subscribe_park *bool
Channel_sync_interval *string
Connections *[]*FsConnJsonCfg
}
// Represents one connection instance towards FreeSWITCH
type FsConnJsonCfg struct {
Server *string
Password *string
Reconnects *int
}
// SM-Kamailio config section
type SmKamJsonCfg struct {
Enabled *bool
Rater *string
Cdrs *string
Create_cdr *bool
Debit_interval *string
Min_call_duration *string
Max_call_duration *string
Connections *[]*KamConnJsonCfg
}
// Represents one connection instance towards Kamailio
type KamConnJsonCfg struct {
Evapi_addr *string
Reconnects *int
}
// SM-OpenSIPS config section
type SmOsipsJsonCfg struct {
Enabled *bool
Listen_udp *string
Rater *string
Cdrs *string
Create_cdr *bool
Debit_interval *string
Min_call_duration *string
Max_call_duration *string
Events_subscribe_interval *string
Mi_addr *string
}
// Represents one connection instance towards OpenSIPS
type OsipsConnJsonCfg struct {
Mi_addr *string
Reconnects *int
}
// History server config section
type HistServJsonCfg struct {
Enabled *bool
History_dir *string
Save_interval *string
}
// PubSub server config section
type PubSubServJsonCfg struct {
Enabled *bool
}
// Aliases server config section
type AliasesServJsonCfg struct {
Enabled *bool
}
// Users server config section
type UserServJsonCfg struct {
Enabled *bool
Indexes *[]string
}
// Mailer config section
type MailerJsonCfg struct {
Server *string
Auth_user *string
Auth_passwd *string
From_address *string
}

View File

@@ -0,0 +1,106 @@
/*
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 config
import (
"flag"
"github.com/cgrates/cgrates/utils"
"testing"
)
var testLocal = flag.Bool("local", false, "Perform the tests only on local test environment, disabled by default.") // This flag will be passed here via "go test -local" args
var mfCgrCfg *CGRConfig
func TestMfInitConfig(t *testing.T) {
if !*testLocal {
return
}
var err error
if mfCgrCfg, err = NewCGRConfigFromFolder("/usr/share/cgrates/conf/samples/multifiles"); err != nil {
t.Fatal("Got config error: ", err.Error())
}
}
func TestMfGeneralItems(t *testing.T) {
if !*testLocal {
return
}
if mfCgrCfg.DefaultReqType != utils.META_PSEUDOPREPAID { // Twice reconfigured
t.Error("DefaultReqType: ", mfCgrCfg.DefaultReqType)
}
if mfCgrCfg.DefaultCategory != "call" { // Not configred, should be inherited from default
t.Error("DefaultCategory: ", mfCgrCfg.DefaultCategory)
}
}
func TestMfCdreDefaultInstance(t *testing.T) {
if !*testLocal {
return
}
for _, prflName := range []string{"*default", "export1"} {
if _, hasIt := mfCgrCfg.CdreProfiles[prflName]; !hasIt {
t.Error("Cdre does not contain profile ", prflName)
}
}
prfl := "*default"
if mfCgrCfg.CdreProfiles[prfl].CdrFormat != "csv" {
t.Error("Default instance has cdrFormat: ", mfCgrCfg.CdreProfiles[prfl].CdrFormat)
}
if mfCgrCfg.CdreProfiles[prfl].DataUsageMultiplyFactor != 1024.0 {
t.Error("Default instance has cdrFormat: ", mfCgrCfg.CdreProfiles[prfl].DataUsageMultiplyFactor)
}
if len(mfCgrCfg.CdreProfiles[prfl].HeaderFields) != 0 {
t.Error("Default instance has number of header fields: ", len(mfCgrCfg.CdreProfiles[prfl].HeaderFields))
}
if len(mfCgrCfg.CdreProfiles[prfl].ContentFields) != 12 {
t.Error("Default instance has number of content fields: ", len(mfCgrCfg.CdreProfiles[prfl].ContentFields))
}
if mfCgrCfg.CdreProfiles[prfl].ContentFields[2].Tag != "Direction" {
t.Error("Unexpected headerField value: ", mfCgrCfg.CdreProfiles[prfl].ContentFields[2].Tag)
}
}
func TestMfCdreExport1Instance(t *testing.T) {
if !*testLocal {
return
}
prfl := "export1"
if mfCgrCfg.CdreProfiles[prfl].CdrFormat != "csv" {
t.Error("Export1 instance has cdrFormat: ", mfCgrCfg.CdreProfiles[prfl].CdrFormat)
}
if mfCgrCfg.CdreProfiles[prfl].DataUsageMultiplyFactor != 1.0 {
t.Error("Export1 instance has DataUsageMultiplyFormat: ", mfCgrCfg.CdreProfiles[prfl].DataUsageMultiplyFactor)
}
if mfCgrCfg.CdreProfiles[prfl].CostRoundingDecimals != 3.0 {
t.Error("Export1 instance has cdrFormat: ", mfCgrCfg.CdreProfiles[prfl].CostRoundingDecimals)
}
if len(mfCgrCfg.CdreProfiles[prfl].HeaderFields) != 2 {
t.Error("Export1 instance has number of header fields: ", len(mfCgrCfg.CdreProfiles[prfl].HeaderFields))
}
if mfCgrCfg.CdreProfiles[prfl].HeaderFields[1].Tag != "RunId" {
t.Error("Unexpected headerField value: ", mfCgrCfg.CdreProfiles[prfl].HeaderFields[1].Tag)
}
if len(mfCgrCfg.CdreProfiles[prfl].ContentFields) != 9 {
t.Error("Export1 instance has number of content fields: ", len(mfCgrCfg.CdreProfiles[prfl].ContentFields))
}
if mfCgrCfg.CdreProfiles[prfl].ContentFields[2].Tag != "Account" {
t.Error("Unexpected headerField value: ", mfCgrCfg.CdreProfiles[prfl].ContentFields[2].Tag)
}
}

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