PyFreeBilling : présentation du softswitch basé sur Freeswitch dédié au wholesale

PyFreeBilling est une solution Open Source de billing et de softswitch dédiée à une activité d’opérateur wholesale VoIP. Cette solution est basée sur Freeswitch, postgresql et python. Ces fonctionnalités sont complètes. PyFreeBilling a été développé avec un objectif de montée en charge (il est actuellement utilisé notament pour terminer les appels des call centers).

Table des matières

Introduction

Il est extrêmement difficile de trouver des solutions Open Source permettant de monter une infrastructure permettant de vendre des communications VoIP, de gérer la facturation (billing) avec une contrainte de montée en charge importante. beaucoup de petits opérateurs utilisent a2billing, mais malgré la qualité du projet, son utilisation est différente : il a été développé pour gérer les cartes d’appels (calling cards).

Explication des choix techniques

Je souhaitais utiliser Freeswitch comme switch VoIP pour ces nombreuses qualités :

  • performance et stabilité assurant une bonne montée en charge
  • performance de la stack SIP (stack Sofia développée par Nokia)
  • utilisation du langage LUA pour les scripts
  • applications performantes
  • fonctions de débuggage évoluées

Ensuite, naturellement la base de données choisie a été PostgreSQL. Je ne vais pas faire un article complet sur ce choix, mais il est important. PostgreSQL est un moteur de base de données performant et fiable, deux qualités indispensables pour cette application.

Enfin, la gestion doit s’effectuer via une interface web, et pour cela le langage python a été choisi : performance, lisibilité … et puis j’aime bien ce langage (c’est important, non ?). Bien entendu, le choix d’un framework comme Django c’est imposé, car il apporte un ensemble de fonctionnalités indispensables (cadre de développement, ORM, sécurité, applications …).

Fonctionnalités

PyFreeBilling intégre un ensemble de fonctionnalités nécessaires à une activité de vente de minutes VoIP :

  • Customer add/modify/delete
  • IP termination
  • SIP authentication
  • Prepaid and/or postpaid
  • Realtime billing
  • Block calls on negative balance (prepaid) or balance under credit limit (postpaid)
  • Block / allow negative margin calls
  • Email alerts
  • Daily balance email to customer
  • Limit the maximum number of calls per customer and/or per gateway
  • Multiple contexts
  • Tons of media handling options
  • Powerfull ratecard engine
  • Provider add/modify/delete
  • Powerful LCR engine
  • Routing based on area code
  • Routing decision based on quality, reliability, cost or load balancing (equal)
  • Limit max channels by each provider gateway
  • Extensive call and financial reporting screens (TBD)
  • CDR export to CSV
  • Design for scalability

Interface web

Toute la gestion s’effectue via l’interface web.

D’autres copies d’écran sont disponibles sur le repo, n’hésitez pas à y aller. Elles ont été créées par ordre de programmation :

  • ajoût d’un client
  • ajoût d’un compte SIP client
  • création d’une grille tarifaire et d’un tarif
  • affectation d’une grille tarifaire à un compte client
  • ajoût d’argent à la balance d’un client
  • création d’une grille tarifaire fournisseur et d’un tarif
  • création d’une LCR (définition du routage d’un appel – très important, c’est le lien entre une grille de vente client et une grille d’achat fournisseur)
  • création d’une gateway fournisseur

Documentation

La documentation est disponible sur readthedocs. La doc utilisateur sera enrichie au fil de l’eau, il me reste encore un peu de travail de rédaction.

Où est le code

Le code est hébergé sur Bitbucket.

Support

Vous pouvez ouvrir vos demandes de support (bug, amélioration, remarque constructive …) sur bitbucket, qui fournit un bel outil de gestion des « issues ».

Et maintenant ?

Je vais bientôt sortir la v1.2. Pour cela, je finis les tests sur un script de déploiement afin de simplifier cette tâche ainsi qu’une intégration dans l’interface web de quelques commandes cli de Freeswitch pour les allegriques de la ligne de commande.

J’ai plein d’idées pour la V2, comme l’intégration de la gestion multi devises, d’émission des factures, de gestion des abonnements et de gestion des appels entrants (DID ou SDA).

N’hésitez pas à commenter, émettre des suggestions, j’attache beaucoup d’importante aux retours.

PyFreeBilling : wholesale billing application documentation

You will find the documentation here : http://pyfreebilling.readthedocs.org/en/latest/index.html

It will update as new functionnalities will be added or new version released.

PyFreeBilling is a open source wholesale billing application based on Freeswitch.

PyFreeBilling : migration to bitbucket

I recently decided to migrate the PyFreeBilling project (wholesale softwitch based on freeswitch) from Github to Bitbucket. The new address is https://bitbucket.org/mwolff/pyfreebilling

Why ? because i like BitBucket and overall the tools for managing issues. I used extensively for my other projects.

Install freeswitch compiled with odbc PostgreSQL on debian wheezy

This is my first howto in english.

As I will release in some days, my project name pyfreebilling (wholesale voip platefom based on Freeswitch, PostgeSQL and Django), i write the first installation step : howto compile and install freeswitch / odbc and postgresql on debian wheezy.

Table des matières

Prerequisites

You need to install these packages :

apt-get install git-core build-essential autoconf automake libtool libncurses5 libncurses5-dev gawk libjpeg-dev zlib1g-dev pkg-config libssl-dev libpq-dev unixodbc-dev odbc-postgresql postgresql postgresql-client

Configuration

You download freeswitch using git :

git clone git://git.freeswitch.org/freeswitch.git

go to the new freeswitch directory and start bootstrap :

./bootstrap.sh

and configure :

./configure

After, you need to edit modules.conf to selected required modules. For pyfreebilling, you need these modules :

applications/mod_commands
applications/mod_db
applications/mod_dptools
applications/mod_esf
applications/mod_esl
applications/mod_expr
applications/mod_fifo
applications/mod_fsv
applications/mod_hash
applications/mod_memcache
applications/mod_nibblebill
applications/mod_spandsp
codecs/mod_amr
codecs/mod_bv
codecs/mod_b64
codecs/mod_g723_1
codecs/mod_g729
codecs/mod_h26x
codecs/mod_vp8
codecs/mod_ilbc
codecs/mod_speex
dialplans/mod_dialplan_xml
endpoints/mod_loopback
endpoints/mod_sofia
event_handlers/mod_cdr_pg_csv
event_handlers/mod_event_socket
formats/mod_local_stream
formats/mod_native_file
formats/mod_sndfile
formats/mod_tone_stream
languages/mod_lua
languages/mod_spidermonkey
#languages/mod_yaml
loggers/mod_console
loggers/mod_logfile
loggers/mod_syslog
xml_int/mod_xml_rpc
xml_int/mod_xml_scgi

you also need to edit the Makefile of cdr_pg_csv module. The file si located here : src/mod/event_handlers/mod_cdr_pg_csv/Makefile . Copy and past the following code :

UNAME := $(shell uname -s)
ifeq ($(UNAME),SunOS)
ISA64 := $(shell isainfo -n)
LOCAL_CFLAGS=-I/usr/include/postgresql
ifneq (,$(findstring m64,$(CFLAGS)))
LOCAL_LDFLAGS=-L/usr/pgsql-9.1/lib/$(ISA64) -R/usr/pgsql-9.1/lib/$(ISA64) -lpq -static
else
LOCAL_LDFLAGS=-L/usr/pgsql-9.1/lib -R/usr/pgsql-9.1/lib -lpq -static
endif
else
LOCAL_CFLAGS=-I/usr/include/postgresql
LOCAL_LDFLAGS=-L/usr/pgsql-9.1/lib -lpq -static
endif
include ../../../../build/modmake.rules

Compilation & installation

Now, we are ready for make, so do :

make & make install

Now we need to create a freeswitch user :

adduser --disabled-password  --quiet --system --home /usr/local/freeswitch --gecos "FreeSWITCH Voice Platform" --ingroup daemon freeswitch
and to apply the rule to freeswitch user :
chown -R freeswitch:daemon /usr/local/freeswitch/ 
chmod -R o-rwx /usr/local/freeswitch/

Init script

and now, we need to create the init script to start and stop freeswitch :

nano /etc/init.d/freeswitch
#!/bin/bash
### BEGIN INIT INFO
# Provides:          freeswitch
# Required-Start:    $local_fs $remote_fs
# Required-Stop:     $local_fs $remote_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Description:       Freeswitch debian init script.
# Author:            Matthew Williams
#
### END INIT INFO
# Do NOT "set -e"

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
DESC="Freeswitch"
NAME=freeswitch
DAEMON=/usr/local/freeswitch/bin/$NAME
DAEMON_ARGS="-nc"
PIDFILE=/usr/local/freeswitch/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME

FS_USER=freeswitch
FS_GROUP=freeswitch

# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions

#
# Function that sets ulimit values for the daemon
#
do_setlimits() {
        ulimit -c unlimited
        ulimit -d unlimited
        ulimit -f unlimited
        ulimit -i unlimited
        ulimit -n 999999
        ulimit -q unlimited
        ulimit -u unlimited
        ulimit -v unlimited
        ulimit -x unlimited
        ulimit -s 240
        ulimit -l unlimited
        return 0
}

#
# Function that starts the daemon/service
#
do_start()
{
    # Set user to run as
        if [ $FS_USER ] ; then
      DAEMON_ARGS="`echo $DAEMON_ARGS` -u $FS_USER"
        fi
    # Set group to run as
        if [ $FS_GROUP ] ; then
          DAEMON_ARGS="`echo $DAEMON_ARGS` -g $FS_GROUP"
        fi

        # Return
        #   0 if daemon has been started
        #   1 if daemon was already running
        #   2 if daemon could not be started
        start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null -- 
                || return 1
        do_setlimits
        start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --background -- 
                $DAEMON_ARGS 
                || return 2
        # Add code here, if necessary, that waits for the process to be ready
        # to handle requests from services started subsequently which depend
        # on this one.  As a last resort, sleep for some time.
}

#
# Function that stops the daemon/service
#
do_stop()
{
        # Return
        #   0 if daemon has been stopped
        #   1 if daemon was already stopped
        #   2 if daemon could not be stopped
        #   other if a failure occurred
        start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
        RETVAL="$?"
        [ "$RETVAL" = 2 ] && return 2
        # Wait for children to finish too if this is a daemon that forks
        # and if the daemon is only ever run from this initscript.
        # If the above conditions are not satisfied then add some other code
        # that waits for the process to drop all resources that could be
        # needed by services started subsequently.  A last resort is to
        # sleep for some time.
        start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
        [ "$?" = 2 ] && return 2
        # Many daemons don't delete their pidfiles when they exit.
        rm -f $PIDFILE
        return "$RETVAL"
}

#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
        #
        # If the daemon can reload its configuration without
        # restarting (for example, when it is sent a SIGHUP),
        # then implement that here.
        #
        start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
        return 0
}

case "$1" in
  start)
        [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
        do_start
        case "$?" in
                0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
        esac
        ;;
  stop)
        [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
        do_stop
        case "$?" in
                0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
        esac
        ;;
  status)
       status_of_proc -p $PIDFILE $DAEMON $NAME && exit 0 || exit $?
       ;;
  #reload|force-reload)
        #
        # If do_reload() is not implemented then leave this commented out
        # and leave 'force-reload' as an alias for 'restart'.
        #
        #log_daemon_msg "Reloading $DESC" "$NAME"
        #do_reload
        #log_end_msg $?
        #;;
  restart|force-reload)
        #
        # If the "reload" option is implemented then remove the
        # 'force-reload' alias
        #
        log_daemon_msg "Restarting $DESC" "$NAME"
        do_stop
        case "$?" in
          0|1)
                do_start
                case "$?" in
                        0) log_end_msg 0 ;;
                        1) log_end_msg 1 ;; # Old process is still running
                        *) log_end_msg 1 ;; # Failed to start
                esac
                ;;
          *)
                # Failed to stop
                log_end_msg 1
                ;;
        esac
        ;;
  *)
        #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
        echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
        exit 3
        ;;
esac

exit 0

mate this script executable :

chmod +x /etc/init.d/freeswitch
update-rc.d freeswitch defaults

And now ?

Enjoy !

But, don’t forget, you need to configure freeswitch and PostgreSQL (i will write an article explaining how to do)