Application LCDial() – Least Cost Routing / FailOver application for Asterisk 1.4 and 1.6
Description
LCDial is a very powerful and versatile solution for Least Cost Routing/FailOver which gets information about call routing from 2 tables stored into a MySQL database.
Since release 770, it permits to specify an argument that established which paths the outgoing call should take: this is a very important feature for PBX that is shared by different companies.
Page Contents
- How does it do
- Tables structure
- Using LCDial() on PBX for a single company (r770 o above)
- Using LCDial() on PBX for 3 different companies (r770 or above)
- Download LCDial application
- Support for LCDial application
- Original implementation of LCDial(), by Wolfgang Pichler
- Synopsis
- Description
- Parameters:
- Return codes
- mysql Database Layout
- Configuration
- Get it
- Compile It
- Help
- Management Interface
- See also
The source code for the application with some examples and an automatic installation script is available for free, released under GNU GPL license.
The MySQL database should contain 2 tables:
- lcdial_providers, within the provider name, dial string, and a flag to enable/disable it
- lcdial_rates, within the regexp prefix, provider name, rate, and a comment
In this way is very very simple to compile the LCR database by hand, using phpmyadmin or other tool: you can insert a complete lcdial_rates table with several provider costs, and enable it into lcdial_providers table only the available providers.
How does it do
- In extensions.conf you need to dial a call using LCDial() instead of Dial() application
- table lcdial_rates will be scan to extract all prefixes which match (using REGEXP) to the destination number
- for each provider, if more than one record match, the longest will be taken (i.e. calling 0039328 matches both ^0039 and ^00393, so only the rate corresponding to ^00393 will be taken into account)
- only enabled provider will be evaluated: this feature permits to manage least cost routing using a long lcdial_rates table with the costs for every provider and enabling into lcdial_providers only the telephony provider really accessible (both VoIP, PSTN, Mobile)
- provider will be sorted from the cheapest to the most expensive
- Dial() application will be invoked for the #1 enabled provider (the cheapest)
- if the call will be answered, at the end of the application return; if the call will not be answered, the Dial() application will be invoked for the #2 enabled provider (less cheaper than #1), and so on.
As you can see, in this way the LCDial() application provides a FailOver mechanism so if provider #1 does not work, the 2nd one will be tried automatically, then the 3rd one, and so on.
Tables structure
The lcdial_providers define the list of providers. Look at the enabled field: freevoip is disabled (enabled=0), company 1 uses fastweb, wind-leonardo, voipvoice, voipvoice_backup (enabled=1), company2 uses fastweb and voipvoice (enabled=2).
id | provider | dialstr | enabled | ratename | ratemul |
---|---|---|---|---|---|
1 | fastweb | Zap/g1/%s | 1 | fastweb | 1 |
2 | freevoip | IAX2/freevoip/%s | 0 | freevoip | 1 |
3 | wind-leonardo | SIP/%s@100 | 1 | wind-leonardo | 1 |
4 | voipvoice | IAX2/voipvoice1/%s | 1 | voipvoice | 1 |
5 | voipvoice_backup | IAX2/voipvoice2/%s | 1 | voipvoice | 1.2 |
6 | company2_fastweb | Zap/g2/%s | 2 | fastweb | 1 |
7 | company2_voipvoice | IAX2/voipvoice3/%s | 2 | voipvoice | 1 |
prefix | ratename | rate | note |
---|---|---|---|
^005521 | freevoip | 0.03 | brazil rio de janeiro |
^32……. | wind-leonardo | 0 | wind cellulars |
^3[3469]……. | wind-leonardo | 0.06 | italian cellulars (not wind) |
Using LCDial() on PBX for a single company (r770 o above)
[trunknational]
; National context accessed through trunk
exten => _0ZXXX.,1,NoOp(National outcall)
exten => _0ZXXX.,n,LCDial(1,${EXTEN},90)
exten => _0ZXXX.,n,Hangup
Using LCDial() on PBX for 3 different companies (r770 or above)
This feature is useful for PBX used by more than one company, so you can define that users 11,12,13,14,15 calls through the providers where the enabled field (in providers table) is 1, while users 51,52,53,54 calls through the providers which has enabled=2, and users 61,62,63 calls through the providers which has enabled=3
This can be done for up to 10 routes, between 1 and 9.
If the first parameter is not specified, 1 is assumed.
LCDIAL_PATH_2=51,52,53,54
LCDIAL_PATH_3=61,62,63
[macro-LCDialSetPath]
; set the variable "LCDialPath" (first argument of LCDial) to select
; the path for the outgoing call
exten => s,1,Set(LCDialPath=1)
;exten => s,n,MacroExit
; select the path
exten => s,n,GotoIf($["a${LCDIAL_PATH_2}"="a"]?:20)
; LCDIAL_PATH_2 exists: check that the extension that originate the call is inside LCDIAL_PATH_2
exten => s,n,MacroExit
exten => s,20,GotoIf(${REGEX("${CALLERID(number)}" ${LCDIAL_PATH_2})}?:40)
exten => s,n,Set(LCDialPath=2)
exten => s,n,MacroExit
exten => s,20,GotoIf(${REGEX("${CALLERID(number)}" ${LCDIAL_PATH_3})}?:60)
exten => s,n,Set(LCDialPath=3)
exten => s,n,MacroExit
exten => 60,MacroExit
[trunknational]
; National context accessed through trunk
exten => _0ZXXX.,1,NoOp(National outcall)
exten => _0ZXXX.,n,Macro(LCDialSetPath) ; set the path to call to (enable=1 or enable=2 or...)
exten => _0ZXXX.,n,LCDial(${LCDialPath},${EXTEN},90)
exten => _0ZXXX.,n,Hangup
Original implementation of LCDial(), by Wolfgang Pichler
Synopsis
Does a mysql Database lookup to find the cheapest provider for a given destination number, and then it invokes the dial command. If the channel is unavailable then it will fall back to the next configured Provider.
Description
- LCDial(number to dial,timeout,options,URL) — dial one channel
Parameters:
- number to dial specifies the number which should get dial.
- all other options are the same as within the standard Asterisk cmd Dial command (the are directly passed to the dial command)
Return codes
Behaves exactly like Asterisk cmd Dial
Example:
exten => 4000,1,LCDial(${EXTEN},15)
For an up to date LCDial application, please look below at the “Improved version of LCDial() for Asterisk” section!
mysql Database Layout
table | description |
provider | Informations about each Provider (name,id,dialstr,status) |
providerdestination | Which destinations (countryprefixid) are available with which provider |
providerrate | The tariffrate for the given provider / destination |
countryprefix | Table with dial codes |
country | optional – not really needed – but if you want to have nice names for the countries |
CREATE TABLE `bit_countryprefix` (
`countryprefixid` int(11) NOT NULL auto_increment,
`prefix` text NOT NULL,
`countrycode` char(3) default NULL,
`subcode` varchar(10) NOT NULL default ”,
PRIMARY KEY (`countryprefixid`)
) TYPE=MyISAM;
CREATE TABLE `bit_provider` (
`providerid` int(11) NOT NULL auto_increment,
`providername` text NOT NULL,
`dialstr` varchar(200) NOT NULL default ”,
`status` tinyint(3) unsigned NOT NULL default ‘0’,
PRIMARY KEY (`providerid`)
) TYPE=MyISAM;
CREATE TABLE `bit_providerdestination` (
`providerdestinationid` int(11) NOT NULL auto_increment,
`countryprefixid` int(11) NOT NULL default ‘0’,
`providerid` int(11) NOT NULL default ‘0’,
PRIMARY KEY (`providerdestinationid`)
) TYPE=MyISAM;
CREATE TABLE `bit_providerrate` (
`providerrateid` int(11) NOT NULL auto_increment,
`providerid` int(11) NOT NULL default ‘0’,
`countryprefixid` int(11) NOT NULL default ‘0’,
`rate` double NOT NULL default ‘0’,
PRIMARY KEY (`providerrateid`)
) TYPE=MyISAM;
CREATE TABLE `bit_country` (
`countrycode` char(3) default NULL,
`countryname` text NOT NULL,
`countryid` int(11) default NULL
) TYPE=MyISAM;
Configuration
LCDial needs a configuration file called lcdial.conf in the standard asterisk configuration directory.
Example Configuration:
[global]
hostname=localhost
dbname=asterisk
user=asterisk
password=asterisk
port=3306
[sql]
getproviders=SELECT providerrate.rate, dialstr, countryprefix.countryprefixid, provider.providerid, provider.providername FROM countryprefix LEFT JOIN providerdestination USING ( countryprefixid ) LEFT JOIN provider USING ( providerid ) LEFT JOIN providerrate ON concat( provider.providerid, countryprefix.countryprefixid ) = concat( providerrate.providerid, providerrate.countryprefixid ) WHERE prefix = substring( '%s', 1, length( prefix ) ) AND provider.status=0 ORDER BY length( prefix ) DESC , providerrate.rate ASC
Compile It
It assumes that the asterisk sources can be found at /usr/src/asterisk, that the asterisk modules directory is at /usr/lib/asterisk/modules and that the asterisk configuration is at /etc/asterisk/.
For compiling it you need to have already installed asterisk 😉 – and the mysql client library and header files.
From asterisk stable to asterisk cvs head the channel structure has changed a little bit – so if you want to compile against asterisk stable then you have to comment this “CFLAGS+=-DUSE_CVS” line to get it to compile (else you would get an error like “error: structure has no member named `cid'”
Help
If you are having trouble getting the application up and running and need professional help, feel free to drop us a line at [email protected]
Management Interface
If you are looking for a management frontend or a turn-key-solution please get in touch with us: [email protected]