Skip to content

Commit 2b930d7

Browse files
InterLinked1Friendly Automation
authored and
Friendly Automation
committed
cdr: Allow bridging and dial state changes to be ignored.
Allows bridging, parking, and dial messages to be globally ignored for all CDRs such that only a single CDR record is generated per channel. This is useful when CDRs should endure for the lifetime of an entire channel and bridging and dial updates in the dialplan should not result in multiple CDR records being created for the call. With the ignore bridging option, bridging changes have no impact on the channel's CDRs. With the ignore dial state option, multiple Dials and their outcomes have no impact on the channel's CDRs. The last disposition on the channel is preserved in the CDR, so the actual disposition of the call remains available. These two options can reduce the amount of "CDR hacks" that have hitherto been necessary to ensure that CDR was not "spoiled" by these messages if that was undesired, such as putting a dummy optimization-disabled local channel between the caller and the actual call and putting the CDR on the channel in the middle to ensure that CDR would persist for the entire call and properly record start, answer, and end times. Enabling these options is desirable when calls correspond to the entire lifetime of channels and the CDR should reflect that. Current default behavior remains unchanged. ASTERISK-30091 #close Change-Id: I393981af42732ec5ac3ff9266444abb453b7c832
1 parent f0586da commit 2b930d7

File tree

4 files changed

+96
-9
lines changed

4 files changed

+96
-9
lines changed

configs/samples/cdr.conf.sample

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@
3232
; is "no".
3333
;congestion = no
3434

35+
; Define whether or not to ignore bridging changes in CDRs. This prevents
36+
; bridging changes from resulting in multiple CDRs for different parts of
37+
; a call. Default is "no". This setting cannot be changed on a reload.
38+
;ignorestatechanges = no
39+
40+
; Define whether or not to ignore dial updates in CDRs. This prevents
41+
; dial updates from resulting in multiple CDRs for different parts of
42+
; a call. The last disposition on the channel will be used for the CDR.
43+
; Use with caution. Default is "no".
44+
;ignoredialchanges = no
45+
3546
; Normally, CDR's are not closed out until after all extensions are finished
3647
; executing. By enabling this option, the CDR will be ended before executing
3748
; the "h" extension and hangup handlers so that CDR values such as "end" and

doc/CHANGES-staging/cdr_ignore.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Subject: cdr
2+
3+
Two new options have been added which allow
4+
bridging and dial state changes to be ignored
5+
in CDRs, which can be useful if a single CDR
6+
is desired for a channel.

include/asterisk/cdr.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ enum ast_cdr_settings {
225225
CDR_INITIATED_SECONDS = 1 << 5, /*!< Include microseconds into the billing time */
226226
CDR_DEBUG = 1 << 6, /*!< Enables extra debug statements */
227227
CDR_CHANNEL_DEFAULT_ENABLED = 1 << 7, /*!< Whether CDR is enabled for each channel by default */
228+
CDR_IGNORE_STATE_CHANGES = 1 << 8, /*!< Whether to ignore bridge and other call state change events */
229+
CDR_IGNORE_DIAL_CHANGES = 1 << 9, /*!< Whether to ignore dial state changes */
228230
};
229231

230232
/*! \brief CDR Batch Mode settings */

main/cdr.c

Lines changed: 77 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,29 @@
111111
to undisable (enable) CDR for a call.</para>
112112
</description>
113113
</configOption>
114+
<configOption name="ignorestatechanges" default="no">
115+
<synopsis>Whether CDR is updated or forked by bridging changes.</synopsis>
116+
<description><para>Define whether or not CDR should be updated by bridging changes.
117+
This includes entering and leaving bridges and call parking.</para>
118+
<para>If this is set to "no", bridging changes will be ignored for all CDRs.
119+
This should only be done if these events should not affect CDRs and are undesired,
120+
such as to use a single CDR for the lifetime of the channel.</para>
121+
<para>This setting cannot be changed on a reload.</para>
122+
</description>
123+
</configOption>
124+
<configOption name="ignoredialchanges" default="no">
125+
<synopsis>Whether CDR is updated or forked by dial updates.</synopsis>
126+
<description><para>Define whether or not CDR should be updated by dial updates.</para>
127+
<para>If this is set to "no", a single CDR will be used for the channel, even if
128+
multiple endpoints or destinations are dialed sequentially. Note that you will also
129+
lose detailed nonanswer dial dispositions if this option is enabled, which may not be acceptable,
130+
e.g. instead of detailed no-answer dispositions like BUSY and CONGESTION, the disposition
131+
will always be NO ANSWER if the channel was unanswered (it will still be ANSWERED
132+
if the channel was answered).</para>
133+
<para>This option should be enabled if a single CDR is desired for the lifetime of
134+
the channel.</para>
135+
</description>
136+
</configOption>
114137
<configOption name="unanswered">
115138
<synopsis>Log calls that are never answered and don't set an outgoing party.</synopsis>
116139
<description><para>
@@ -208,6 +231,8 @@
208231
#define DEFAULT_END_BEFORE_H_EXTEN "1"
209232
#define DEFAULT_INITIATED_SECONDS "0"
210233
#define DEFAULT_CHANNEL_ENABLED "1"
234+
#define DEFAULT_IGNORE_STATE_CHANGES "0"
235+
#define DEFAULT_IGNORE_DIAL_CHANGES "0"
211236

212237
#define DEFAULT_BATCH_SIZE "100"
213238
#define MAX_BATCH_SIZE 1000
@@ -222,6 +247,7 @@
222247
} while (0)
223248

224249
static int cdr_debug_enabled;
250+
static int dial_changes_ignored;
225251

226252
#define CDR_DEBUG(fmt, ...) \
227253
do { \
@@ -2170,6 +2196,10 @@ static void handle_dial_message(void *data, struct stasis_subscription *sub, str
21702196
if (!it_cdr->fn_table->process_dial_begin) {
21712197
continue;
21722198
}
2199+
if (dial_changes_ignored) {
2200+
CDR_DEBUG("%p - Ignoring Dial Begin message\n", it_cdr);
2201+
continue;
2202+
}
21732203
CDR_DEBUG("%p - Processing Dial Begin message for channel %s, peer %s\n",
21742204
it_cdr,
21752205
caller ? caller->base->name : "(none)",
@@ -2181,6 +2211,12 @@ static void handle_dial_message(void *data, struct stasis_subscription *sub, str
21812211
if (!it_cdr->fn_table->process_dial_end) {
21822212
continue;
21832213
}
2214+
if (dial_changes_ignored) {
2215+
/* Set the disposition, and do nothing else. */
2216+
it_cdr->disposition = dial_status_to_disposition(dial_status);
2217+
CDR_DEBUG("%p - Setting disposition and that's it (%s)\n", it_cdr, dial_status);
2218+
continue;
2219+
}
21842220
CDR_DEBUG("%p - Processing Dial End message for channel %s, peer %s\n",
21852221
it_cdr,
21862222
caller ? caller->base->name : "(none)",
@@ -2192,15 +2228,19 @@ static void handle_dial_message(void *data, struct stasis_subscription *sub, str
21922228
}
21932229
}
21942230

2195-
/* If no CDR handled a dial begin message, make a new one */
2196-
if (res && ast_strlen_zero(dial_status)) {
2197-
struct cdr_object *new_cdr;
2231+
/* If we're ignoring dial changes, don't allow multiple CDRs for this channel. */
2232+
if (!dial_changes_ignored) {
2233+
/* If no CDR handled a dial begin message, make a new one */
2234+
if (res && ast_strlen_zero(dial_status)) {
2235+
struct cdr_object *new_cdr;
21982236

2199-
new_cdr = cdr_object_create_and_append(cdr, stasis_message_timestamp(message));
2200-
if (new_cdr) {
2201-
new_cdr->fn_table->process_dial_begin(new_cdr, caller, peer);
2237+
new_cdr = cdr_object_create_and_append(cdr, stasis_message_timestamp(message));
2238+
if (new_cdr) {
2239+
new_cdr->fn_table->process_dial_begin(new_cdr, caller, peer);
2240+
}
22022241
}
22032242
}
2243+
22042244
ao2_unlock(cdr);
22052245
ao2_cleanup(cdr);
22062246
}
@@ -4200,6 +4240,8 @@ static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_
42004240
ast_cli(a->fd, " Log calls by default: %s\n", ast_test_flag(&mod_cfg->general->settings, CDR_CHANNEL_DEFAULT_ENABLED) ? "Yes" : "No");
42014241
ast_cli(a->fd, " Log unanswered calls: %s\n", ast_test_flag(&mod_cfg->general->settings, CDR_UNANSWERED) ? "Yes" : "No");
42024242
ast_cli(a->fd, " Log congestion: %s\n\n", ast_test_flag(&mod_cfg->general->settings, CDR_CONGESTION) ? "Yes" : "No");
4243+
ast_cli(a->fd, " Ignore bridging changes: %s\n\n", ast_test_flag(&mod_cfg->general->settings, CDR_IGNORE_STATE_CHANGES) ? "Yes" : "No");
4244+
ast_cli(a->fd, " Ignore dial state changes: %s\n\n", ast_test_flag(&mod_cfg->general->settings, CDR_IGNORE_DIAL_CHANGES) ? "Yes" : "No");
42034245
if (ast_test_flag(&mod_cfg->general->settings, CDR_BATCHMODE)) {
42044246
ast_cli(a->fd, "* Batch Mode Settings\n");
42054247
ast_cli(a->fd, " -------------------\n");
@@ -4379,6 +4421,8 @@ static int process_config(int reload)
43794421
aco_option_register(&cfg_info, "size", ACO_EXACT, general_options, DEFAULT_BATCH_SIZE, OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_cdr_config, batch_settings.size), 0, MAX_BATCH_SIZE);
43804422
aco_option_register(&cfg_info, "time", ACO_EXACT, general_options, DEFAULT_BATCH_TIME, OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_cdr_config, batch_settings.time), 1, MAX_BATCH_TIME);
43814423
aco_option_register(&cfg_info, "channeldefaultenabled", ACO_EXACT, general_options, DEFAULT_CHANNEL_ENABLED, OPT_BOOLFLAG_T, 1, FLDSET(struct ast_cdr_config, settings), CDR_CHANNEL_DEFAULT_ENABLED);
4424+
aco_option_register(&cfg_info, "ignorestatechanges", ACO_EXACT, general_options, DEFAULT_IGNORE_STATE_CHANGES, OPT_BOOLFLAG_T, 1, FLDSET(struct ast_cdr_config, settings), CDR_IGNORE_STATE_CHANGES);
4425+
aco_option_register(&cfg_info, "ignoredialchanges", ACO_EXACT, general_options, DEFAULT_IGNORE_DIAL_CHANGES, OPT_BOOLFLAG_T, 1, FLDSET(struct ast_cdr_config, settings), CDR_IGNORE_DIAL_CHANGES);
43824426
}
43834427

43844428
if (aco_process_config(&cfg_info, reload) == ACO_PROCESS_ERROR) {
@@ -4541,6 +4585,7 @@ static int unload_module(void)
45414585

45424586
static int load_module(void)
45434587
{
4588+
struct module_config *mod_cfg = NULL;
45444589
if (process_config(0)) {
45454590
return AST_MODULE_LOAD_FAILURE;
45464591
}
@@ -4561,13 +4606,36 @@ static int load_module(void)
45614606
return AST_MODULE_LOAD_FAILURE;
45624607
}
45634608

4609+
mod_cfg = ao2_global_obj_ref(module_configs);
4610+
45644611
stasis_message_router_add(stasis_router, ast_channel_snapshot_type(), handle_channel_snapshot_update_message, NULL);
4612+
4613+
/* Always process dial messages, because even if we ignore most of it, we do want the dial status for the disposition. */
45654614
stasis_message_router_add(stasis_router, ast_channel_dial_type(), handle_dial_message, NULL);
4566-
stasis_message_router_add(stasis_router, ast_channel_entered_bridge_type(), handle_bridge_enter_message, NULL);
4567-
stasis_message_router_add(stasis_router, ast_channel_left_bridge_type(), handle_bridge_leave_message, NULL);
4568-
stasis_message_router_add(stasis_router, ast_parked_call_type(), handle_parked_call_message, NULL);
4615+
if (!mod_cfg || !ast_test_flag(&mod_cfg->general->settings, CDR_IGNORE_DIAL_CHANGES)) {
4616+
dial_changes_ignored = 0;
4617+
} else {
4618+
dial_changes_ignored = 1;
4619+
CDR_DEBUG("Dial messages will be mostly ignored\n");
4620+
}
4621+
4622+
/* If explicitly instructed to ignore call state changes, then ignore bridging events, parking, etc. */
4623+
if (!mod_cfg || !ast_test_flag(&mod_cfg->general->settings, CDR_IGNORE_STATE_CHANGES)) {
4624+
stasis_message_router_add(stasis_router, ast_channel_entered_bridge_type(), handle_bridge_enter_message, NULL);
4625+
stasis_message_router_add(stasis_router, ast_channel_left_bridge_type(), handle_bridge_leave_message, NULL);
4626+
stasis_message_router_add(stasis_router, ast_parked_call_type(), handle_parked_call_message, NULL);
4627+
} else {
4628+
CDR_DEBUG("All bridge and parking messages will be ignored\n");
4629+
}
4630+
45694631
stasis_message_router_add(stasis_router, cdr_sync_message_type(), handle_cdr_sync_message, NULL);
45704632

4633+
if (mod_cfg) {
4634+
ao2_cleanup(mod_cfg);
4635+
} else {
4636+
ast_log(LOG_WARNING, "Unable to obtain CDR configuration during module load?\n");
4637+
}
4638+
45714639
active_cdrs_master = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
45724640
AST_NUM_CHANNEL_BUCKETS, cdr_master_hash_fn, NULL, cdr_master_cmp_fn);
45734641
if (!active_cdrs_master) {

0 commit comments

Comments
 (0)