<p>Nir Simionovich (GreenfieldTech - Israel) has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/6816">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">This is the actual commit... didn't add the new files in...<br><br>Change-Id: I6b7d2964ae5b39a35ec23da460a725e6c0d7c214<br>---<br>A cdr/cdr_beanstalkd.c<br>A configs/samples/cdr_beanstalkd.conf.sample<br>2 files changed, 404 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/16/6816/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/cdr/cdr_beanstalkd.c b/cdr/cdr_beanstalkd.c<br>new file mode 100644<br>index 0000000..e70b047<br>--- /dev/null<br>+++ b/cdr/cdr_beanstalkd.c<br>@@ -0,0 +1,383 @@<br>+/*<br>+ * Asterisk -- An open source telephony toolkit.<br>+ *<br>+ * Copyright (C) 2004 - 2005<br>+ *<br>+ * See http://www.asterisk.org for more information about<br>+ * the Asterisk project. Please do not directly contact<br>+ * any of the maintainers of this project for assistance;<br>+ * the project provides a web site, mailing lists and IRC<br>+ * channels for your use.<br>+ *<br>+ * This program is free software, distributed under the terms of<br>+ * the GNU General Public License Version 2. See the LICENSE file<br>+ * at the top of the source tree.<br>+ */<br>+<br>+/*!<br>+ * \file<br>+ * \brief Asterisk Beanstalkd CDR records.<br>+ *<br>+ * See also<br>+ * \arg \ref AstCDR<br>+ * \ingroup cdr_drivers<br>+ */<br>+<br>+/*! \li \ref cdr_beanstalkd.c uses the configuration file \ref cdr_beanstalkd.conf<br>+ * \addtogroup configuration_file Configuration Files<br>+ */<br>+<br>+/*!<br>+ * \page cdr_beanstalkd.conf cdr_beanstalkd.conf<br>+ * \verbinclude cdr_beanstalkd.conf.sample<br>+ */<br>+<br>+/*** MODULEINFO<br>+ <depend>beanstalk</depend><br>+ ***/<br>+<br>+/*** DOCUMENTATION<br>+ <managerEvent language="en_US" name="Cdr"><br>+ <managerEventInstance class="EVENT_FLAG_CDR"><br>+ <synopsis>Raised when a CDR is generated.</synopsis><br>+ <syntax><br>+ <parameter name="AccountCode"><br>+ <para>The account code of the Party A channel.</para><br>+ </parameter><br>+ <parameter name="Source"><br>+ <para>The Caller ID number associated with the Party A in the CDR.</para><br>+ </parameter><br>+ <parameter name="Destination"><br>+ <para>The dialplan extension the Party A was executing.</para><br>+ </parameter><br>+ <parameter name="DestinationContext"><br>+ <para>The dialplan context the Party A was executing.</para><br>+ </parameter><br>+ <parameter name="CallerID"><br>+ <para>The Caller ID name associated with the Party A in the CDR.</para><br>+ </parameter><br>+ <parameter name="Channel"><br>+ <para>The channel name of the Party A.</para><br>+ </parameter><br>+ <parameter name="DestinationChannel"><br>+ <para>The channel name of the Party B.</para><br>+ </parameter><br>+ <parameter name="LastApplication"><br>+ <para>The last dialplan application the Party A executed.</para><br>+ </parameter><br>+ <parameter name="LastData"><br>+ <para><br>+ The parameters passed to the last dialplan application the<br>+ Party A executed.<br>+ </para><br>+ </parameter><br>+ <parameter name="StartTime"><br>+ <para>The time the CDR was created.</para><br>+ </parameter><br>+ <parameter name="AnswerTime"><br>+ <para><br>+ The earliest of either the time when Party A answered, or<br>+ the start time of this CDR.<br>+ </para><br>+ </parameter><br>+ <parameter name="EndTime"><br>+ <para><br>+ The time when the CDR was finished. This occurs when the<br>+ Party A hangs up or when the bridge between Party A and<br>+ Party B is broken.<br>+ </para><br>+ </parameter><br>+ <parameter name="Duration"><br>+ <para>The time, in seconds, of <replaceable>EndTime</replaceable> - <replaceable>StartTime</replaceable>.</para><br>+ </parameter><br>+ <parameter name="BillableSeconds"><br>+ <para>The time, in seconds, of <replaceable>AnswerTime</replaceable> - <replaceable>StartTime</replaceable>.</para><br>+ </parameter><br>+ <parameter name="Disposition"><br>+ <para>The final known disposition of the CDR.</para><br>+ <enumlist><br>+ <enum name="NO ANSWER"><br>+ <para>The channel was not answered. This is the default disposition.</para><br>+ </enum><br>+ <enum name="FAILED"><br>+ <para>The channel attempted to dial but the call failed.</para><br>+ <note><br>+ <para>The congestion setting in <filename>cdr.conf</filename> can result<br>+ in the <literal>AST_CAUSE_CONGESTION</literal> hang up cause or the<br>+ <literal>CONGESTION</literal> dial status to map to this disposition.<br>+ </para><br>+ </note><br>+ </enum><br>+ <enum name="BUSY"><br>+ <para>The channel attempted to dial but the remote party was busy.</para><br>+ </enum><br>+ <enum name="ANSWERED"><br>+ <para>The channel was answered. The hang up cause will no longer<br>+ impact the disposition of the CDR.</para><br>+ </enum><br>+ <enum name="CONGESTION"><br>+ <para>The channel attempted to dial but the remote party was congested.</para><br>+ </enum><br>+ </enumlist><br>+ </parameter><br>+ <parameter name="AMAFlags"><br>+ <para>A flag that informs a billing system how to treat the CDR.</para><br>+ <enumlist><br>+ <enum name="OMIT"><br>+ <para>This CDR should be ignored.</para><br>+ </enum><br>+ <enum name="BILLING"><br>+ <para>This CDR contains valid billing data.</para><br>+ </enum><br>+ <enum name="DOCUMENTATION"><br>+ <para>This CDR is for documentation purposes.</para><br>+ </enum><br>+ </enumlist><br>+ </parameter><br>+ <parameter name="UniqueID"><br>+ <para>A unique identifier for the Party A channel.</para><br>+ </parameter><br>+ <parameter name="UserField"><br>+ <para><br>+ A user defined field set on the channels. If set on both the Party A<br>+ and Party B channel, the userfields of both are concatenated and<br>+ separated by a <literal>;</literal>.<br>+ </para><br>+ </parameter><br>+ </syntax><br>+ <description><br>+ <para><br>+ The <replaceable>Cdr</replaceable> job is only raised when the<br>+ <filename>cdr_beanstalk</filename> backend is loaded and registered with<br>+ the CDR engine.<br>+ </para><br>+ <note><br>+ <para><br>+ This job can contain additional fields depending on the configuration<br>+ provided by <filename>cdr_beanstalkd.conf</filename>.<br>+ </para><br>+ </note><br>+ </description><br>+ </managerEventInstance><br>+ </managerEvent><br>+ ***/<br>+<br>+#include "beanstalk.h"<br>+<br>+#include "asterisk.h"<br>+<br>+#include <time.h><br>+#include <stdio.h><br>+<br>+#include "asterisk/channel.h"<br>+#include "asterisk/cdr.h"<br>+#include "asterisk/module.h"<br>+#include "asterisk/utils.h"<br>+#include "asterisk/manager.h"<br>+#include "asterisk/config.h"<br>+#include "asterisk/pbx.h"<br>+<br>+#define DATE_FORMAT "%Y-%m-%d %T"<br>+#define CONF_FILE "cdr_beanstalkd.conf"<br>+#define CUSTOM_FIELDS_BUF_SIZE 1024<br>+#define BEANSTALK_JOB_SIZE 4096<br>+#define BEANSTALK_JOB_PRIORITY 99<br>+#define BEANSTALK_JOB_TTR 60<br>+#define BEANSTALK_JOB_DELAY 0<br>+<br>+static const char name[] = "cdr_beanstalkd";<br>+<br>+static int enablecdr = 0;<br>+static char *bs_host = "127.0.0.1";<br>+static int bs_port = 13000;<br>+static char *bs_tube = "asterisk-cdr";<br>+static int priority = BEANSTALK_JOB_PRIORITY;<br>+<br>+static struct ast_str *customfields;<br>+AST_RWLOCK_DEFINE_STATIC(customfields_lock);<br>+<br>+static int beanstalk_put(struct ast_cdr *cdr);<br>+<br>+static int load_config(int reload) {<br>+ char *cat = NULL;<br>+ struct ast_config *cfg;<br>+ struct ast_variable *v;<br>+ struct ast_flags config_flags = {reload ? CONFIG_FLAG_FILEUNCHANGED : 0};<br>+ int newenablecdr = 0;<br>+<br>+ cfg = ast_config_load(CONF_FILE, config_flags);<br>+ if (cfg == CONFIG_STATUS_FILEUNCHANGED) {<br>+ return 0;<br>+ }<br>+<br>+ if (cfg == CONFIG_STATUS_FILEINVALID) {<br>+ ast_log(LOG_ERROR, "Config file '%s' could not be parsed\n", CONF_FILE);<br>+ return -1;<br>+ }<br>+<br>+ if (!cfg) {<br>+ /* Standard configuration */<br>+ ast_log(LOG_WARNING, "Failed to load configuration file. Module not activated.\n");<br>+ if (enablecdr) {<br>+ ast_cdr_backend_suspend(name);<br>+ }<br>+ enablecdr = 0;<br>+ return -1;<br>+ }<br>+<br>+ if (reload) {<br>+ ast_rwlock_wrlock(&customfields_lock);<br>+ }<br>+<br>+ if (reload && customfields) {<br>+ ast_free(customfields);<br>+ customfields = NULL;<br>+ }<br>+<br>+ while ((cat = ast_category_browse(cfg, cat))) {<br>+ if (!strcasecmp(cat, "general")) {<br>+ v = ast_variable_browse(cfg, cat);<br>+ while (v) {<br>+ if (!strcasecmp(v->name, "enabled"))<br>+ newenablecdr = ast_true(v->value);<br>+<br>+ if (!newenablecdr) {<br>+ enablecdr = newenablecdr;<br>+ return 0;<br>+ }<br>+<br>+ if (!strcasecmp(v->name, "host"))<br>+ bs_host = ast_strdup(v->value);<br>+ if (!strcasecmp(v->name, "port"))<br>+ bs_port = atoi(v->value);<br>+ if (!strcasecmp(v->name, "tube"))<br>+ bs_tube = ast_strdup(v->value);<br>+ if (!strcasecmp(v->name, "priority"))<br>+ priority = atoi(v->value);<br>+<br>+ v = v->next;<br>+ }<br>+ }<br>+ ast_log(LOG_NOTICE, "Added beanstalkd server %s at port %d with tube %s", bs_host, bs_port, bs_tube);<br>+ }<br>+<br>+ if (reload) {<br>+ ast_rwlock_unlock(&customfields_lock);<br>+ }<br>+<br>+ ast_config_destroy(cfg);<br>+<br>+ if (!newenablecdr) {<br>+ ast_cdr_backend_suspend(name);<br>+ } else if (newenablecdr) {<br>+ ast_cdr_backend_unsuspend(name);<br>+ }<br>+ enablecdr = newenablecdr;<br>+<br>+ return 0;<br>+}<br>+<br>+static int beanstalk_put(struct ast_cdr *cdr) {<br>+ struct ast_tm timeresult;<br>+ char strStartTime[80] = "";<br>+ char strAnswerTime[80] = "";<br>+ char strEndTime[80] = "";<br>+ char buf[CUSTOM_FIELDS_BUF_SIZE];<br>+ char cdr_buffer[BEANSTALK_JOB_SIZE];<br>+<br>+ if (!enablecdr)<br>+ return 0;<br>+<br>+ int bs_id, bs_socket = bs_connect(bs_host, bs_port);<br>+<br>+ if (bs_use(bs_socket, bs_tube) != BS_STATUS_OK) {<br>+ ast_log(LOG_ERROR, "Connection to Beanstalk tube %s @ %s:%d had failed", bs_tube, bs_host, bs_port);<br>+ return 0;<br>+ }<br>+<br>+ ast_localtime(&cdr->start, &timeresult, NULL);<br>+ ast_strftime(strStartTime, sizeof(strStartTime), DATE_FORMAT, &timeresult);<br>+<br>+ if (cdr->answer.tv_sec) {<br>+ ast_localtime(&cdr->answer, &timeresult, NULL);<br>+ ast_strftime(strAnswerTime, sizeof(strAnswerTime), DATE_FORMAT, &timeresult);<br>+ }<br>+<br>+ ast_localtime(&cdr->end, &timeresult, NULL);<br>+ ast_strftime(strEndTime, sizeof(strEndTime), DATE_FORMAT, &timeresult);<br>+<br>+ buf[0] = '\0';<br>+ cdr_buffer[0] = '\0';<br>+<br>+ ast_rwlock_rdlock(&customfields_lock);<br>+ if (customfields && ast_str_strlen(customfields)) {<br>+ struct ast_channel *dummy = ast_dummy_channel_alloc();<br>+ if (!dummy) {<br>+ ast_log(LOG_ERROR, "Unable to allocate channel for variable substitution.\n");<br>+ return 0;<br>+ }<br>+ ast_channel_cdr_set(dummy, ast_cdr_dup(cdr));<br>+ pbx_substitute_variables_helper(dummy, ast_str_buffer(customfields), buf, sizeof(buf) - 1);<br>+ ast_channel_unref(dummy);<br>+ }<br>+ ast_rwlock_unlock(&customfields_lock);<br>+<br>+ snprintf(cdr_buffer, BEANSTALK_JOB_SIZE,<br>+ "{ \"AccountCode\": \"%s\", \"Source\": \"%s\", \"Destination\": \"%s\", \"DestinationContext\": \"%s\", "<br>+ "\"CallerID\": \"%s\", \"Channel\": \"%s\", \"DestinationChannel\": \"%s\", \"LastApplication\": \"%s\", "<br>+ "\"LastData\": \"%s\", \"StartTime\": \"%s\", \"AnswerTime\": \"%s\", \"EndTime\": \"%s\", "<br>+ "\"Duration\": \"%ld\", \"BillableSeconds\": \"%ld\", \"Disposition\": \"%s\", \"AMAFlags\": \"%s\", "<br>+ "\"UniqueID\": \"%s\", \"UserField\": \"%s\", \"CustomFields\": \"%s\" }",<br>+ cdr->accountcode, cdr->src, cdr->dst, cdr->dcontext, cdr->clid, cdr->channel,<br>+ cdr->dstchannel, cdr->lastapp, cdr->lastdata, strStartTime, strAnswerTime, strEndTime,<br>+ cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition),<br>+ ast_channel_amaflags2string(cdr->amaflags), cdr->uniqueid, cdr->userfield, buf<br>+ );<br>+<br>+ bs_id = bs_put(bs_socket, priority, BEANSTALK_JOB_DELAY, BEANSTALK_JOB_TTR, cdr_buffer, strlen(cdr_buffer));<br>+<br>+ if (bs_id > 0)<br>+ ast_log(LOG_NOTICE, "Successfully created job %d with %s\n", bs_id, cdr_buffer);<br>+ else<br>+ ast_log(LOG_ERROR, "CDR job creation failed for %s\n", cdr_buffer);<br>+<br>+ bs_disconnect(bs_socket);<br>+ return 0;<br>+}<br>+<br>+static int unload_module(void) {<br>+ if (ast_cdr_unregister(name)) {<br>+ return -1;<br>+ }<br>+<br>+ if (customfields)<br>+ ast_free(customfields);<br>+<br>+ return 0;<br>+}<br>+<br>+static int load_module(void) {<br>+ if (ast_cdr_register(name, "Asterisk CDR Beanstalkd Backend", beanstalk_put)) {<br>+ return AST_MODULE_LOAD_DECLINE;<br>+ }<br>+<br>+ if (load_config(0)) {<br>+ ast_cdr_unregister(name);<br>+ return AST_MODULE_LOAD_DECLINE;<br>+ }<br>+<br>+ return AST_MODULE_LOAD_SUCCESS;<br>+}<br>+<br>+static int reload(void) {<br>+ return load_config(1);<br>+}<br>+<br>+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Asterisk Beanstalkd CDR Backend",<br>+.support_level = AST_MODULE_SUPPORT_CORE,<br>+.load = load_module,<br>+.unload = unload_module,<br>+.reload = reload,<br>+.load_pri = AST_MODPRI_CDR_DRIVER,<br>+);<br>+<br>diff --git a/configs/samples/cdr_beanstalkd.conf.sample b/configs/samples/cdr_beanstalkd.conf.sample<br>new file mode 100644<br>index 0000000..368ac6c<br>--- /dev/null<br>+++ b/configs/samples/cdr_beanstalkd.conf.sample<br>@@ -0,0 +1,21 @@<br>+;<br>+; Asterisk Call Management CDR via Beanstalkd job queue<br>+;<br>+; Beanstalkd is a simple job queue server, that is highly versatile and simple to use.<br>+; Beanstalkd includes the capability of using multiple queues at the same time, with priorities.<br>+<br>+[general]<br>+enabled = no<br>+<br>+host = 127.0.0.1 ; Specify the remote IP address of the Beanstalkd server<br>+port = 11300 ; Specify the remote PORT of the the Beanstalkd server<br>+tube = asterisk-cdr ; Specify the default CDR job queue to use<br>+priority = 99 ; Specify the default job priority for the queue. This parameter is useful when building<br>+ ; platform with multiple Asterisk servers, that are used for different functions. For example,<br>+ ; none billable CDR records can be inserted with a priority of 99, while billable ones be<br>+ ; inserted with a priority of 1<br>+<br>+<br>+<br>+<br>+<br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/6816">change 6816</a>. To unsubscribe, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/6816"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>
<div style="display:none"> Gerrit-Change-Id: I6b7d2964ae5b39a35ec23da460a725e6c0d7c214 </div>
<div style="display:none"> Gerrit-Change-Number: 6816 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Nir Simionovich (GreenfieldTech - Israel) <nirs@greenfieldtech.net> </div>