viernes, 25 de noviembre de 2011

Monitor de llamadas concurrentes

En esta oportunidad, quiero compartir con ustedes un pequeño desarrollo que hice, para poder ver gráficamente el historial de llamadas concurrentes que tuvo nuestra central, a lo largo del día.

El funcionamiento de la aplicación es muy simple, lo único que hace es tomar muestras (de la cantidad de llamadas activas en el momento de la medición) y guardarlas en un archivo de texto, con la fecha y hora. Luego esta información es procesada y mostrada en un gráfico, como se muestra a continuación:


En esta primer versión la herramienta debe instalarse en la misma PBX, dado a que la toma de información la hace localmente, a través de comandos de la CLI. En futuras versiones posiblemente se adapte para que funcione de forma remota.

Para bajar la aplicación pueden hacerlo desde:

http://ccallsmonitor.googlecode.com/files/ccallsmonitor.tar.gz

Hasta la próxima!!

viernes, 11 de noviembre de 2011

Entendiendo el CDR

El Call Detail Record (Registro Detallado de Llamadas) es una aplicación que se encarga de almacenar el Log de llamadas que son cursadas a través de una PBX. En el presente artículo intentaré explicar su instalación, configuración y entendimiento de los registros para poder identificar los distintos tipos de llamadas.

Instalación:

Antes de comenzar con la instalación, es importante aclarar que, para realizar este artículo se utilizó Asterisk 1.4.37 y Addons 1.4.7, que son las versiones que mejores resultados me trajeron, hasta ahora.

Luego de descargar, descomprimir y entrar al menú de selección de la instalación de Asterisk(utilizando: make menuconfig), podemos acceder a la opción "Call Detail Recording", donde está la lista de módulos que permiten que el CDR almacene los logs en el formato / aplicación que necesitemos.

La siguiente tabla muestra las opciones que nos permite utilizar el CDR, para almacenar los registros de las llamadas, y las dependencias que necesita para que las mismas funcionen.

Applicación
Descripción
Dependencia
cdr_csvComma Separated Values CDR BackendN/A
cdr_customCustomizable Comma Separated Values CDR BackendN/A
cdr_managerAsterisk Manager Interface CDR BackendN/A
cdr_odbcODBC CDR Backendunixodbc, ltdl
cdr_pgsqlPostgreSQL CDR Backendpgsql
cdr_radiusRADIUS CDR Backendradius
cdr_sqliteSQLite CDR Backendsqlite
cdr_tdsMSSQL CDR Backendfreetds

Nótese que en la lista no se encuentra el soporte para mysql, no hay que preocuparse, porque este se encuentra en el paquete addons.

Finalizada la selección de las características para el CDR y de la instalación de Asterisk, pasaremos a instalar el paquete Addons, para poder incorporar el soporte para MySQL.

Hacemos los mismos pasos que antes, descargar, descomprimir y entrar al menú de selección y finalmente accedemos a la opción "Call Detail Recording", donde nos muestra: cdr_addon_mysql (En caso que no esté instalada la dependencia: mysqlclient, aparecerá con XXX al principio, por lo tanto debemos salir del menú e instalar dicha dependencia para luego volver a este paso.)

Configuración:

Una vez finalizado el proceso de instalación, haremos un recorrido por todos los archivos de configuración del CDR y finalmente nos centraremos en la configuración de la integración con MySQL.

La siguiente tabla muestra, la lista de archivos que permiten configurar las diferentes prestaciones del CDR:

Archivo
Descripción
cdr.confConfiguración general del CDR
cdr_manager.confAcceso al CDR a través del AMI
cdr_pgsql.confConfiguración para PostgresSQL
cdr_custom.confConfiguración de Log Personalizado
cdr_mysql.confConfiguración para MySQL
cdr_odbc.confConector ODBC (Para cualquier base de datos)
cdr_tds.confConfiguración para FreeTDS

cdr.conf

En este archivo vamos a cargar todas las configuraciones que son a nivel general, las cuales afectarían a todos los módulos que anteriormente mencionamos, ahora vamos a ver sus parámetros.

[general]
Parámetro
Valores
Descripción
enableyes/noActiva o desactiva la utilización del CDR.
unansweredyes/noRegistrar las llamadas no atendidas
batchyes/noPermite almacenar los datos de las llamadas en un buffer, para luego enviarlos a donde quisiéramos almacenar. OJO! que activando esta opción, si bien se liberaría un poco de procesamiento a Asterisk, puede ocasionar posibles perdidas de datos, en caso que Asterisk se detenga.
sizeNúmero enteroEn caso que se active la opción de Batch, se define la cantidad de registros de CDR que se acumularán en el buffer, antes de ser enviados al medio de almacenamiento.
timeNúmero enteroEn caso que se active la opción de Batch, se define la cantidad de tiempo máximo que deberán estar los registros almacenados en el buffer.
timeNúmero enteroEn caso que se active la opción de Batch, se define la cantidad de tiempo máximo que deberán estar los registros almacenados en el buffer.
scheduleronlyyes/noAsterisk utiliza un "scheduler" interno, para determinar en que momentos se envían los registros al medio de almacenamiento. Dicho envío se puede hacer en el mismo "thread" donde está el "scheduler" o bien crear un nuevo "thread" para realizar esta tarea. En caso que se configure un proceso Batch con pocos registros, el envío puede estar en el "thread" del "scheduler" (Configurar este parámetro en "yes"). Ahora si se configuró un Batch de mayor tamaño, por ejemplo size=10, es recomendable realizar esta tarea en un nuevo "thread" (Configurar este parámetro en "no").
safeshutdownyes/noSi hacemos un "stop now" en Asterisk y justo en ese momento se están enviando registros de CDR, al configurar este parámetro en "yes", se bloquea el apagado de Asterisk hasta que terminen de enviarse los registros.
endbeforehextenyes/noGeneralmente el CDR no se cierra hasta que todos los internos finalicen su ejecución. Al habilitar esta opción el CDR se cerrará antes que se ejecute el hangup, lo que causaría que se pierdan algunos datos.

cdr_manager.conf

Permite acceder al CDR a través del AMI, para ello cuenta con una sola opción a configurar, que la vemos a continuación:

[General]
enabled = yes / no

cdr_pgsql.conf

Para activar el almacenamiento del CDR en PostgresSQL, se tendrá que ingresar los siguientes parámetros, que corresponden a los datos de acceso al servidor de Base de datos.

[global]
hostname=localhost
port=5432
dbname=asterisk
password=password
user=postgres
table=cdr

cdr_custom.conf

Aquí podremos personalizar en el formato, del archivo de texto plano, que queremos que se almacenen los registros de las llamadas. Estos serán guardados en el siguiente path: /var/log/asterisk/cdr-custom/Master.csv

[mappings]
Master.csv => ${CSV_QUOTE(${CDR(clid)})},${CSV_QUOTE(${CDR(src)})},${CSV_QUOTE(${CDR(dst)})},${CSV_QUOTE(${CDR(dcontext)})},${CSV_QUOTE(${CDR(channel)})},${CSV_QUOTE(${CDR(dstchannel)})},${CSV_QUOTE(${CDR(lastapp)})},${CSV_QUOTE(${CDR(lastdata)})},${CSV_QUOTE(${CDR(start)})},${CSV_QUOTE(${CDR(answer)})},${CSV_QUOTE(${CDR(end)})},${CSV_QUOTE(${CDR(duration)})},${CSV_QUOTE(${CDR(billsec)})},${CSV_QUOTE(${CDR(disposition)})},${CSV_QUOTE(${CDR(amaflags)})},${CSV_QUOTE(${CDR(accountcode)})},${CSV_QUOTE(${CDR(uniqueid)})},${CSV_QUOTE(${CDR(userfield)})}

En la línea anterior podemos cambiar el nombre del archivo, elegir que datos guardar y que separador utilizar.

cdr_mysql.conf

Al igual que con PostgresSQL, debemos configurar los datos de acceso al Servidor.

[global]
hostname = localhost
dbname=asteriskcdrdb
password = amp109
user = asteriskuser
userfield=1
port=3306

cdr_odbc.conf

Igual que los anteriores, excepto que aquí se configura el DSN que contiene los datos del servidor de base de datos al cual queremos almacenar los datos.

[global]
dsn=MySQL-test
username=username
password=password
loguniqueid=yes
dispositionstring=yes
table=cdr
usegmtime=no

Tabla de registro del CDR para SQL

En caso que utilicemos un servidor de base de datos, cualquier de los que soporta Asterisk, vamos a necesitar crear la tabla donde se almacenaran los datos. Para ello, presento a continuación el código SQL para crear la tabla:

CREATE TABLE `cdr` (
`calldate` datetime NOT NULL default '0000-00-00 00:00:00',
`clid` varchar(80) NOT NULL default '',
`src` varchar(80) NOT NULL default '',
`dst` varchar(80) NOT NULL default '',
`dcontext` varchar(80) NOT NULL default '',
`channel` varchar(80) NOT NULL default '',
`dstchannel` varchar(80) NOT NULL default '',
`lastapp` varchar(80) NOT NULL default '',
`lastdata` varchar(80) NOT NULL default '',
`duration` int(11) NOT NULL default '0',
`billsec` int(11) NOT NULL default '0',
`disposition` varchar(45) NOT NULL default '',
`amaflags` int(11) NOT NULL default '0',
`accountcode` varchar(20) NOT NULL default '',
`userfield` varchar(255) NOT NULL default '',
`uniqueid` VARCHAR(32) NOT NULL default ''
);

Ahora bien, para que las consultas sobre la tabla del CDR sean un poco más rápidas, lo que haremos es crear unos índices sobre las columnas que seguramente más utilizaremos:

ALTER TABLE `cdr` ADD INDEX ( `calldate` );
ALTER TABLE `cdr` ADD INDEX ( `dst` );
ALTER TABLE `cdr` ADD INDEX ( `uniqueid` );

Algunas consultas SQL

Bien, como frutilla del postre a continuación pondré algunos Queryes que tal vez sirvan para obtener distintos tipos de reportes, sobre el CDR. Obviamente estos dependen de la configuración de cada PBX.

- Cantidad de llamadas atendidas por día:
select date_format(calldate, "%Y-%m-%d"), count(*) from cdr where dcontext = 'from-pstn' group by 1;

- Cantidad de llamadas exitosas realizadas por día:
select date_format(calldate, "%Y-%m-%d"), count(*)  from cdr where dstchannel like 'DAHDI%' and disposition = 'ANSWERED' group by 1;

- Cantidad de llamadas fallidas por día:
select date_format(calldate, "%Y-%m-%d"), count(*)  from cdr where dstchannel like 'DAHDI%' and disposition = 'FAILED' group by 1;

- Cantidad de llamadas realizadas por un interno:
select * from cdr where dstchannel like 'DAHDI%' and disposition = 'ANSWERED' and calldate >= 'YYYY-MM-DD 00:00:00' and calldate <= 'YYYY-MM-DD 23;59;59' and src = NUMERO_INTERNO;

reemplazar YY-MM-DD por el día que deseamos consultar y NUMERO_INTERNO por la extensión.

Saludos y hasta la próxima!!

viernes, 4 de noviembre de 2011

Como enviar variables entre centrales

Después de tanto tiempo que no publico nada en el Blog, quiero compartir con ustedes una solución, tal vez un poco "sucia", a un problema que seguramente se le presentó a aquellas personas que tienen varios Asterisk interconectados por troncales SIP o  IAX2.

Veamos un ejemplo:



Tal como se muestra en la imagen anterior, en el momento que Teléfono llama al Interno, dicho llamado pasará por ambas PBX y a su vez generará un registro en el CDR de cada una de ellas. Si queremos asociar ambos registros, nos daríamos cuenta que sería una tarea muy complicada, dado a que ambos contarán con un UniqueID distinto.

Ahora, si nosotros pudiéramos, de alguna forma, enviar de la PBX A a la PBX B el uniqueID, eso simplificaría muchísimo las cosas.

En el momento que el llamado pasa de la "PBX A" a la "PBX B" la única variable que es enviada es CALLERID(name), por lo tanto lo que podríamos hacer es concatenarle los datos que quisiéramos enviar y luego, cuando los valores se reciben en la "PBX B", parsearlos y volver todo como estaba.

Para ello haremos lo siguiente:

PBX A:
Supongamos que en esta PBX tenemos un DID donde los llamados que se reciben a través de este, son enviados a un contexto, llamémoslo from-my-did, que es donde se hace el dial a la PBX B. Entonces para poder concatenar, en este caso el uniqueID, para pasarlo a la otra PBX, tendríamos que poner, antes del dial, la siguiente línea:

exten => s,1,set(CALLERID(name)=${CALLERID(name)}#CDR(uniqueid)=${CDR(uniqueid)}#)
dando como resultado, lo siguiente:

[from-my-did]
exten => s,1,set(CALLERID(name)=${CALLERID(name)}#CDR(uniqueid)=${CDR(uniqueid)}#)
exten => s,n,dial(IAX2/pbxb/frommydid)

Como se podrá notar, cuando concatenamos la variable uniqueID vemos que la misma está entre dos numerales (#), esto se debe a que lo vamos a utilizar como separador para poder parsear los datos que enviamos.

PBX B:
Ahora bien, supongamos que en la configuración del troncal IAX2 dijimos que los llamados de la PBX A, van a ser recibidos en el contexto "from-pbx-a". Entonces tendríamos que agregar la siguiente línea, que se encargará de parsear los datos y crear las variables necesarias:

exten => frommydid,1,AGI(parser.php)

Dando como resultado, lo siguiente:

[from-pbx-a]
exten => frommydid,1,AGI(parser.php)
exten => frommydid,n,Dial(SIP/222)

Por lo tanto, cada vez que la PBX B reciba un llamado desde la PBX A, se parsearan y crearan las variables que concatenamos al CALLERID(name) y luego el llamado será enviado al interno 222.

Aquí les paso el código del parser.php

#!/usr/bin/php
/* Parser.php
 * ----------------------------------------------------------------------
 * AGI utilizado para parsear las variables enviadas a através del
 * CALLERID(name), separadas por #, y luego declararlas en Asterisk.
 * ----------------------------------------------------------------------
 * by Gustavo Borgoni - gborgoni@gmail.com
 */

<?php
require_once "phpagi.php";
$agi = new AGI();

function get_var($value) {
        global $agi;
        $r = $agi->get_variable( $value );
        if ($r['result'] == 1) {
                $result = $r['data'];
                return $result;
        } else {
                return '';
        }
}

$info = get_var("CALLERID(name)");
$result = split("#", $info);
$agi->set_variable("CALLERID(name)", $result[0]);

foreach ($result as $k => $v) {
        $variable = split("=",$v);
        if (trim($variable[0]) != "" && trim($variable[1]) != "") {
                $agi->set_variable(trim($variable[0]), trim($variable[1]));
        }
}

?>


Hasta la próxima!