Good day.
Anyone who administers Asterisk in the slightest degree is faced with the task of combining several servers among themselves. Here it does not matter which protocol IAX or SIP is chosen, since regardless of the protocol there will be approximately the same set of actions. This is no problem as long as your server can be counted on the fingers of one hand. If you didn’t have one hand, and your fingers for the second are already ending then you are welcome under the cut in order to see one of the ways to solve this problem.
I work in one relatively small organization, but this organization is very scattered geographically, we have many small offices and everyone needs telephony, respectively, Asterisk is installed in each office and all servers are integrated to make calls within the company. When a new office opens, you have to configure all the servers and inform them about the additional numbering and the new server. At one point, setting up a regular server, I thought that this could be automated and get rid of most routine actions. According to the results I came to the conclusion that it should be automated:
- Adding a new IAX channel.
- Change parameters of one IAX channel
- Changing the numbering in any office
- No changes required on other servers
Asterisk has already got everything to implement our plans, we just need to configure and link all the components together. So, what do we need to implement our plans:
')
- Asterisk - I use Asterisk 13 LTS Certified
- Realtime - Asterisk needs to be built with the pbx_realtime module
- Lua - I only use it in Asterisk (pbx_lua)
- Perl - Where to without Perl in automation, but can be replaced by any other language
- PostgreSql - I am using version 9.6. Maybe any other database
- Bucardo - I use it for database replication, there may be a regular database tool
Algorithm
For those who use a different set of software, I will describe just an algorithm and then move on to implementing it on the above software set.
There is nothing outstanding here, the algorithm is simple as a stick. Asterisk is configured to use realtime configuration. Adding information about the new IAX channel occurs by adding a row to the appropriate database table. A script is run on the server at regular intervals, which checks the presence of new IAX channels in the database and, if they are detected, performs the registration of all channels. When making a call to an internal number, it checks to which server the given internal number belongs and sends the call to the corresponding server. That's the whole algorithm, see below for more details.
Training
Asterisk . There is no point in describing a complete and detailed installation, you can find more than a dozen articles on the web how to do it correctly and quickly. I will describe only what modules should be installed to solve our problem:
- pbx_realtime
- pbx_lua
- res_config_odbc
- res_odbc
- res_odbc_transaction
These modules should be enough to solve our problem, naturally we do not forget to connect all the modules you need personally. Also install the packages that will be needed to build Asterisk:
yum install -y dmidecode ncurses-devel libxml2-devel openssl-devel newt-devel sqlite-devel libuuid-devel gtk2-devel jansson-devel binutils-devel curl-devel unixODBC unixODBC-devel spandsp-devel spandsp iksemel iksemel-devel libtool-ltdl libtool-ltdl-devel kernel-devel lua-devel perl-CPAN perl-DBD-Pg perl-DBI perl-App-cpanminus
Do not forget that you will need a compiler and all related applications, I usually install as follows:
yum groupinstall -y "development tools"
So we put everything we need. Then you can proceed with the installation of Asterisk, and we will proceed to the configuration.
Realtime . First we need to configure ODBC, it will be Asterisk who will use it to get the settings from the database. Here, in fact, you need to fix only a few files.
/etc/odbc.ini must be converted to this:
[asterisk] Description=PostgreSQL Driver=PostgreSQL Database=asrumoscow Servername=localhost Username=asterisk Password=password Port=5432 Protocol=7.4 Charset=utf8 ReadOnly=No RowVersioning=No ShowSystemTables=No ShowOidColumn=No Trace=No TraceFile=/var/log/asterisk/sql.log FakeOidIndex=No # Asterisk 13.8 Pooling=yes
I think that in a special explanation this file is not needed, specify only your details of access to the database.
/etc/odbcinst.ini we bring to the form:
[PostgreSQL] Description = ODBC for PostgreSQL Driver64 = /usr/pgsql-9.6/lib/psqlodbc.so Setup64 = /usr/pgsql-9.6/lib/psqlodbcw.so FileUsage = 1
If you have another version of PostgreSql, correct the paths to the libraries. This is where the ODBC setup is complete for us.
Go to Asterisk. In order for it to look for settings in the database using the ODBC configured by us, it is necessary to add a block to the /etc/asterisk/res_odbc.conf file:
[realtime-odbc] enabled => yes dsn => asterisk ; /etc/odbc.ini username => asterisk ; password => password ; pre-connect => yes
Now it remains to tell Asterisk which tables and for what it can use. Add the strings to the settings section of the /etc/asterisk/extconfig.conf file:
iax.conf => odbc,realtime-odbc,db_name_config ; iax iaxusers => odbc,realtime-odbc,db_name_iax iaxpeers => odbc,realtime-odbc,db_name_iax sippeers => odbc,realtime-odbc,db_name_sip
db_name_ * - replace with the corresponding tables in the database with which asterisk works
Do not forget to restart Asterisk so that he would take it all. Now you can go directly to the implementation.
Implementation
First we need to figure out how to deliver information on new IAX channels to all Asterisk servers. Given that all our servers use Realtime, we will use the database. To do this, set up a separate server with PostgreSql. Let's create a database in which there will be a table similar to the table from realtime Asterisk. Now my table looks like this:
CREATE TABLE public.iax_peers ( id serial NOT NULL, name character varying(40) COLLATE pg_catalog."default" NOT NULL, type type_values, username character varying(40) COLLATE pg_catalog."default", mailbox character varying(40) COLLATE pg_catalog."default", secret character varying(40) COLLATE pg_catalog."default", dbsecret character varying(40) COLLATE pg_catalog."default", context character varying(40) COLLATE pg_catalog."default", regcontext character varying(40) COLLATE pg_catalog."default", host character varying(40) COLLATE pg_catalog."default", ipaddr character varying(40) COLLATE pg_catalog."default", port integer, defaultip character varying(20) COLLATE pg_catalog."default", sourceaddress character varying(20) COLLATE pg_catalog."default", mask character varying(20) COLLATE pg_catalog."default", regexten character varying(40) COLLATE pg_catalog."default", regseconds integer, accountcode character varying(20) COLLATE pg_catalog."default", mohinterpret character varying(20) COLLATE pg_catalog."default", mohsuggest character varying(20) COLLATE pg_catalog."default", inkeys character varying(40) COLLATE pg_catalog."default", outkeys character varying(40) COLLATE pg_catalog."default", language character varying(10) COLLATE pg_catalog."default", callerid character varying(100) COLLATE pg_catalog."default", cid_number character varying(40) COLLATE pg_catalog."default", sendani yes_no_values, fullname character varying(40) COLLATE pg_catalog."default", trunk yes_no_values, auth character varying(20) COLLATE pg_catalog."default", maxauthreq integer, requirecalltoken iax_requirecalltoken_values, encryption iax_encryption_values, transfer iax_transfer_values, jitterbuffer yes_no_values, forcejitterbuffer yes_no_values, disallow character varying(200) COLLATE pg_catalog."default", allow character varying(200) COLLATE pg_catalog."default", deny character varying(200) COLLATE pg_catalog."default", permit character varying(200) COLLATE pg_catalog."default", codecpriority character varying(40) COLLATE pg_catalog."default", qualify character varying(10) COLLATE pg_catalog."default", qualifysmoothing yes_no_values, qualifyfreqok character varying(10) COLLATE pg_catalog."default", qualifyfreqnotok character varying(10) COLLATE pg_catalog."default", timezone character varying(20) COLLATE pg_catalog."default", adsi yes_no_values, amaflags character varying(20) COLLATE pg_catalog."default", setvar character varying(200) COLLATE pg_catalog."default", CONSTRAINT iax_peers_pkey PRIMARY KEY (id), CONSTRAINT iax_peers_name_key UNIQUE (name) ) WITH ( OIDS = FALSE ) TABLESPACE pg_default; ALTER TABLE public.iax_peers OWNER to asterisk;
The table structure must be completely identical on all servers.
Bucardo . Now we will configure replication of this table. For replication we will use Bucardo, for our task it is more than enough. I will not describe the installation, suppose it is installed and the initial setup is made. It remains for us to only configure the replication of the desired tables.
Add to Bucardo information which databases will participate in replication:
Add a local database which will be our source of all data.
bucardo add database pbxMaster dbname=asterisk dbhost=127.0.0.1 dbuser=bucardo dbpass=Pa$$w0rD
Add a database of a remote server Asterisk
bucardo add database asRUmoscow dbname=asrumoscow dbhost=192.168.0.30 dbuser=bucardo dbpass=Pa$$w0rD
Specify which table will be replicated:
bucardo add table iax_peers --db=pbxMaster relgroup=iax_peer
Create a group for our databases:
bucardo add dbgroup asRealtime
We add our databases to the new group and indicate their purpose.
Specify the data source:
bucardo add dbgroup asRealtime pbxMaster:source
Now we indicate the recipients of this data:
bucardo add dbgroup asRealtime asRUmoscow:target
We inform what and how to synchronize
bucardo add sync asRealtimeSync relgroup=iax_peers dbs=asRealtime
Naturally, bucardo should know about all servers where it is necessary to replicate the table, and there should be 3 or more such servers.
In my case, each server is named in a specific way, for example, asRUmoscow:
- as - says that this is a server with Asterisk.
- RU is the country in which the server is located.
- Moscow is the city where the server is located.
The same name is called the database for Asterisk, it allows you to simplify the scripts.
Since I have the source table name and the recipient table name is always different, you need to inform Bucardo of this, for this we execute the command:
bucardo add customname iax_peers asrumoscow_iax db=asRUmoscow
Here we have indicated that all the data from the iax_peers table replicate to the asrumoscow_iax table and that this applies only to the asRUmoscow database. You can restart Bucardo and verify replication by adding channel information to the iax_peers table, it will be duplicated on all servers.
Perl . Now we need to make the server register new IAX channels. Everything is pretty simple, there is such a
Perl script . Its essence is simple, it starts over the crown every 5 minutes, connects to the database and checks for all IAX channels recorded in the hostname_iax table there are registration lines in the hostname_config table. If something is missing, the script will fix it, naturally, it ignores the IAX channel, the name of which is the hostname of the local server, since registering with yourself the idea is so-so.
Lua . At this stage, it turns out that our channel information is replicated, the servers themselves are registered with each other. It remains only to resolve the issue of call routing, in the normal mode of operation, a new extension is added to the call to the new server, approximately the following content:
exten => _19xx,1,noop( ) exten => _19xx,1,dial(sip/${EXTEN},60,r) exten => _19xx,1,hangup()
If you have 10 servers, then you should add the code above to all servers. It needs to be simplified, for this we will write just one general extension which will itself determine where and how to call. To do this, we will add information about which numbers are served by this or that server in the IAX channels table. For example, for asRUmoscow, the basic information about the IAX channel will look like this:

Here we see in the setvar column a regular expression for which you can check the dialed number by the user to this server or not. This is more than enough to find out where the user wants to call, it remains only to realize this.
For this we have the following code:
extensions = { office = { ["_xxxx"] = internal_sip; }; }
This is not an intricate code that allows you to not edit Asterisk configuration files every time a new server is added.
results
Now we don’t really need to do anything to add a new server. For this you need to run a simple set of commands:
Add information about the new database to the replication system:
bucardo add database asRUsmolensk dbname=asrusmolensk dbhost=192.168.15.30 dbuser=bucardo dbpass=Me%gaP@$$ bucardo add dbgroup asRealtime asRUsmolensk:target bucardo add customname iax_peers asrusmolensk_iax db=asRUsmolensk
Add information about the new IAX channel to the main database:
INSERT INTO public.iax_peers(name, type, username, secret, context, host, trunk, auth, deny, permit, qualify,setvar) VALUES ('asRUsmolensk','friend','asRUsmolensk', 'IAX_PAS$w0Rd','office','dynamic','yes','md5','0.0.0.0/0.0.0.0','192.168.15.30/255.255.255','yes','^[16]6\d{2,2}'),
After that, wait 5 minutes and the IAX channels will rise between all the servers themselves and you can immediately call the new server.
These two actions can be wrapped in the Ansible playbook and then everything will be beautiful and fashionable.
All this allows not only to quickly add new servers, but also to change the routing with the parameters of IAX channels. This has been working for more than one year, now the network has grown to 25 Asterisk servers and is not sure that this is the limit.
If there are comments and suggestions, you are welcome in the comments or drugs.