[Asterisk-code-review] cdr: Allow bridging and dial state changes to be ignored. (asterisk[19])

N A asteriskteam at digium.com
Mon Oct 10 07:36:51 CDT 2022


N A has uploaded this change for review. ( https://gerrit.asterisk.org/c/asterisk/+/19431 )


Change subject: cdr: Allow bridging and dial state changes to be ignored.
......................................................................

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
---
M configs/samples/cdr.conf.sample
A doc/CHANGES-staging/cdr_ignore.txt
M include/asterisk/cdr.h
M main/cdr.c
4 files changed, 134 insertions(+), 9 deletions(-)



  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/31/19431/1

diff --git a/configs/samples/cdr.conf.sample b/configs/samples/cdr.conf.sample
index 6331760..46ddb96 100644
--- a/configs/samples/cdr.conf.sample
+++ b/configs/samples/cdr.conf.sample
@@ -32,6 +32,17 @@
 ; is "no".
 ;congestion = no
 
+; Define whether or not to ignore bridging changes in CDRs. This prevents
+; bridging changes from resulting in multiple CDRs for different parts of
+; a call. Default is "no". This setting cannot be changed on a reload.
+;ignorestatechanges = no
+
+; Define whether or not to ignore dial updates in CDRs. This prevents
+; dial updates from resulting in multiple CDRs for different parts of
+; a call. The last disposition on the channel will be used for the CDR.
+; Use with caution. Default is "no".
+;ignoredialchanges = no
+
 ; Normally, CDR's are not closed out until after all extensions are finished
 ; executing.  By enabling this option, the CDR will be ended before executing
 ; the "h" extension and hangup handlers so that CDR values such as "end" and
diff --git a/doc/CHANGES-staging/cdr_ignore.txt b/doc/CHANGES-staging/cdr_ignore.txt
new file mode 100644
index 0000000..e82f404
--- /dev/null
+++ b/doc/CHANGES-staging/cdr_ignore.txt
@@ -0,0 +1,6 @@
+Subject: cdr
+
+Two new options have been added which allow
+bridging and dial state changes to be ignored
+in CDRs, which can be useful if a single CDR
+is desired for a channel.
diff --git a/include/asterisk/cdr.h b/include/asterisk/cdr.h
index 2bbfbdb..06307c9 100644
--- a/include/asterisk/cdr.h
+++ b/include/asterisk/cdr.h
@@ -225,6 +225,8 @@
 	CDR_INITIATED_SECONDS = 1 << 5,     /*!< Include microseconds into the billing time */
 	CDR_DEBUG = 1 << 6,                 /*!< Enables extra debug statements */
 	CDR_CHANNEL_DEFAULT_ENABLED = 1 << 7, /*!< Whether CDR is enabled for each channel by default */
+	CDR_IGNORE_STATE_CHANGES = 1 << 8,	/*!< Whether to ignore bridge and other call state change events */
+	CDR_IGNORE_DIAL_CHANGES = 1 << 9,	/*!< Whether to ignore dial state changes */
 };
 
 /*! \brief CDR Batch Mode settings */
diff --git a/main/cdr.c b/main/cdr.c
index b059082..8f1f30b 100644
--- a/main/cdr.c
+++ b/main/cdr.c
@@ -111,6 +111,29 @@
 					to undisable (enable) CDR for a call.</para>
 					</description>
 				</configOption>
+				<configOption name="ignorestatechanges" default="no">
+					<synopsis>Whether CDR is updated or forked by bridging changes.</synopsis>
+					<description><para>Define whether or not CDR should be updated by bridging changes.
+					This includes entering and leaving bridges and call parking.</para>
+					<para>If this is set to "no", bridging changes will be ignored for all CDRs.
+					This should only be done if these events should not affect CDRs and are undesired,
+					such as to use a single CDR for the lifetime of the channel.</para>
+					<para>This setting cannot be changed on a reload.</para>
+					</description>
+				</configOption>
+				<configOption name="ignoredialchanges" default="no">
+					<synopsis>Whether CDR is updated or forked by dial updates.</synopsis>
+					<description><para>Define whether or not CDR should be updated by dial updates.</para>
+					<para>If this is set to "no", a single CDR will be used for the channel, even if
+					multiple endpoints or destinations are dialed sequentially. Note that you will also
+					lose detailed nonanswer dial dispositions if this option is enabled, which may not be acceptable,
+					e.g. instead of detailed no-answer dispositions like BUSY and CONGESTION, the disposition
+					will always be NO ANSWER if the channel was unanswered (it will still be ANSWERED
+					if the channel was answered).</para>
+					<para>This option should be enabled if a single CDR is desired for the lifetime of
+					the channel.</para>
+					</description>
+				</configOption>
 				<configOption name="unanswered">
 					<synopsis>Log calls that are never answered and don't set an outgoing party.</synopsis>
 					<description><para>
@@ -208,6 +231,8 @@
 #define DEFAULT_END_BEFORE_H_EXTEN "1"
 #define DEFAULT_INITIATED_SECONDS "0"
 #define DEFAULT_CHANNEL_ENABLED "1"
+#define DEFAULT_IGNORE_STATE_CHANGES "0"
+#define DEFAULT_IGNORE_DIAL_CHANGES "0"
 
 #define DEFAULT_BATCH_SIZE "100"
 #define MAX_BATCH_SIZE 1000
@@ -222,6 +247,7 @@
 	} while (0)
 
 static int cdr_debug_enabled;
+static int dial_changes_ignored;
 
 #define CDR_DEBUG(fmt, ...) \
 	do { \
@@ -2170,6 +2196,10 @@
 			if (!it_cdr->fn_table->process_dial_begin) {
 				continue;
 			}
+			if (dial_changes_ignored) {
+				CDR_DEBUG("%p - Ignoring Dial Begin message\n", it_cdr);
+				continue;
+			}
 			CDR_DEBUG("%p - Processing Dial Begin message for channel %s, peer %s\n",
 				it_cdr,
 				caller ? caller->base->name : "(none)",
@@ -2181,6 +2211,12 @@
 			if (!it_cdr->fn_table->process_dial_end) {
 				continue;
 			}
+			if (dial_changes_ignored) {
+				/* Set the disposition, and do nothing else. */
+				it_cdr->disposition = dial_status_to_disposition(dial_status);
+				CDR_DEBUG("%p - Setting disposition and that's it (%s)\n", it_cdr, dial_status);
+				continue;
+			}
 			CDR_DEBUG("%p - Processing Dial End message for channel %s, peer %s\n",
 				it_cdr,
 				caller ? caller->base->name : "(none)",
@@ -2192,15 +2228,19 @@
 		}
 	}
 
-	/* If no CDR handled a dial begin message, make a new one */
-	if (res && ast_strlen_zero(dial_status)) {
-		struct cdr_object *new_cdr;
+	/* If we're ignoring dial changes, don't allow multiple CDRs for this channel. */
+	if (!dial_changes_ignored) {
+		/* If no CDR handled a dial begin message, make a new one */
+		if (res && ast_strlen_zero(dial_status)) {
+			struct cdr_object *new_cdr;
 
-		new_cdr = cdr_object_create_and_append(cdr, stasis_message_timestamp(message));
-		if (new_cdr) {
-			new_cdr->fn_table->process_dial_begin(new_cdr, caller, peer);
+			new_cdr = cdr_object_create_and_append(cdr, stasis_message_timestamp(message));
+			if (new_cdr) {
+				new_cdr->fn_table->process_dial_begin(new_cdr, caller, peer);
+			}
 		}
 	}
+
 	ao2_unlock(cdr);
 	ao2_cleanup(cdr);
 }
@@ -4200,6 +4240,8 @@
 		ast_cli(a->fd, "  Log calls by default:       %s\n", ast_test_flag(&mod_cfg->general->settings, CDR_CHANNEL_DEFAULT_ENABLED) ? "Yes" : "No");
 		ast_cli(a->fd, "  Log unanswered calls:       %s\n", ast_test_flag(&mod_cfg->general->settings, CDR_UNANSWERED) ? "Yes" : "No");
 		ast_cli(a->fd, "  Log congestion:             %s\n\n", ast_test_flag(&mod_cfg->general->settings, CDR_CONGESTION) ? "Yes" : "No");
+		ast_cli(a->fd, "  Ignore bridging changes:    %s\n\n", ast_test_flag(&mod_cfg->general->settings, CDR_IGNORE_STATE_CHANGES) ? "Yes" : "No");
+		ast_cli(a->fd, "  Ignore dial state changes:  %s\n\n", ast_test_flag(&mod_cfg->general->settings, CDR_IGNORE_DIAL_CHANGES) ? "Yes" : "No");
 		if (ast_test_flag(&mod_cfg->general->settings, CDR_BATCHMODE)) {
 			ast_cli(a->fd, "* Batch Mode Settings\n");
 			ast_cli(a->fd, "  -------------------\n");
@@ -4379,6 +4421,8 @@
 		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);
 		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);
 		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);
+		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);
+		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);
 	}
 
 	if (aco_process_config(&cfg_info, reload) == ACO_PROCESS_ERROR) {
@@ -4541,6 +4585,7 @@
 
 static int load_module(void)
 {
+	struct module_config *mod_cfg = NULL;
 	if (process_config(0)) {
 		return AST_MODULE_LOAD_FAILURE;
 	}
@@ -4561,13 +4606,36 @@
 		return AST_MODULE_LOAD_FAILURE;
 	}
 
+	mod_cfg = ao2_global_obj_ref(module_configs);
+
 	stasis_message_router_add(stasis_router, ast_channel_snapshot_type(), handle_channel_snapshot_update_message, NULL);
+
+	/* Always process dial messages, because even if we ignore most of it, we do want the dial status for the disposition. */
 	stasis_message_router_add(stasis_router, ast_channel_dial_type(), handle_dial_message, NULL);
-	stasis_message_router_add(stasis_router, ast_channel_entered_bridge_type(), handle_bridge_enter_message, NULL);
-	stasis_message_router_add(stasis_router, ast_channel_left_bridge_type(), handle_bridge_leave_message, NULL);
-	stasis_message_router_add(stasis_router, ast_parked_call_type(), handle_parked_call_message, NULL);
+	if (!mod_cfg || !ast_test_flag(&mod_cfg->general->settings, CDR_IGNORE_DIAL_CHANGES)) {
+		dial_changes_ignored = 0;
+	} else {
+		dial_changes_ignored = 1;
+		CDR_DEBUG("Dial messages will be mostly ignored\n");
+	}
+
+	/* If explicitly instructed to ignore call state changes, then ignore bridging events, parking, etc. */
+	if (!mod_cfg || !ast_test_flag(&mod_cfg->general->settings, CDR_IGNORE_STATE_CHANGES)) {
+		stasis_message_router_add(stasis_router, ast_channel_entered_bridge_type(), handle_bridge_enter_message, NULL);
+		stasis_message_router_add(stasis_router, ast_channel_left_bridge_type(), handle_bridge_leave_message, NULL);
+		stasis_message_router_add(stasis_router, ast_parked_call_type(), handle_parked_call_message, NULL);
+	} else {
+		CDR_DEBUG("All bridge and parking messages will be ignored\n");
+	}
+
 	stasis_message_router_add(stasis_router, cdr_sync_message_type(), handle_cdr_sync_message, NULL);
 
+	if (mod_cfg) {
+		ao2_cleanup(mod_cfg);
+	} else {
+		ast_log(LOG_WARNING, "Unable to obtain CDR configuration during module load?\n");
+	}
+
 	active_cdrs_master = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
 		AST_NUM_CHANNEL_BUCKETS, cdr_master_hash_fn, NULL, cdr_master_cmp_fn);
 	if (!active_cdrs_master) {

-- 
To view, visit https://gerrit.asterisk.org/c/asterisk/+/19431
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: 19
Gerrit-Change-Id: I393981af42732ec5ac3ff9266444abb453b7c832
Gerrit-Change-Number: 19431
Gerrit-PatchSet: 1
Gerrit-Owner: N A <mail at interlinked.x10host.com>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20221010/7507da3a/attachment-0001.html>


More information about the asterisk-code-review mailing list