
Asterisk + LUA: quick start
Over the past year, several articles about the use of the lua dialplan in asterisk ( one , two , three , four ) have appeared on Habré . This is an interesting way to write flexible and powerful dialplans. But to try this way of writing dialplans, you need to spend a certain amount of time: install the necessary libraries, rebuild the asterisk with the necessary options.
In addition, many asterisk users have different levels of training: someone is closer to system administration or even to traditional telephony than to programming. Plus, the specifics of telephony - it is better once again with unfamiliar experiments not to load working systems, but to conduct tests and experiments on your laptop - you have to clutter up the system. In general, there are many reasons to “postpone for later.”
In this article I want to show everyone and everyone who works with an asterisk how, using docker, you can quickly get a taste of flexible lua scripts. And then decide whether to use it further in practice or not. (Who is not interested in reading, but interested in watching and listening - at the end of the text is a 6-minute video with the main points and result.)

As part of my several projects, following the current trend of packaging everything in containers, I prepared an image of astolua (asterisk + lua). The Dockerfile contains commands for installing asterisk 11, lua 5.1, luarocks (package manager for lua), luamongo (driver for accessing mongodb), some lua rocks packages. In the future, you can take only useful for yourself in the docker-astolua repository and collect your workhorse.
Undoubtedly, the advantage of the docker is the ability to download an image, conduct tests, experiments, tests, and then remove the images, leaving your operating system clean and familiar.
Based on the astolua image, we will create our own working image, in which we will use the test configuration files for the asterisk and dialplan on lua.
We need docker. If you don’t have it installed, then please install docker first ( official documentation , article on Habr ).
We also need git ( installing git ). I
also note right away that my working system is Ubuntu 14.04. If you use another Linux, then there shouldn’t be any differences in commands, but nuances are not excluded.
We tighten the image (attention, the image will be downloaded from the hub.docker.com repository ~ 600MB in size).
We clone docker-astolua-sample - this is a pre-prepared set of files for this article.
Now let's stop at sample and see the contents of the directory.
Dockerfile
File A file for building our working image. In it, we indicate that we take astolua as a basis. Then add the startup script after_start.sh, which will be executed when the container starts. The asterisk console log will be displayed in the console where we will launch the container.
Build file
within a file docker team to build the image of our sample Dockerfile.
Run
file Inside the file, the docker command to launch the container based on the sample image with the configuration of the resources it needs.
Store
folder The store folder contains the asterisk configuration files (those usually located in / etc / asterisk) and folders for logs and voice menus.
The run command is most interesting because The required resources for the container are indicated here. For example, with the option -v $ (pwd) / store / etc / asterisk: / etc / asterisk we specify that the configuration files from our store folder should be inside the container in their place in / etc / asterisk.
Why are the commands in files? It is convenient to edit commands in files, as this speeds up the time to test changes in teams with different options, and also all changes will fall under version control. And it’s also convenient to transfer options to docker-compose later if the image will be shared with others.
Back to the console.
Let's make an image of astolua: sample (in the directory where we cloned docker-astolua-sample)
We start asterisk (if you already have an asterisk or other service occupying port 5060 already running on the machine, it is better to stop it first)
The asterisk boot log should fall into the console. You can test the connection.
Two subscribers 101 and 102 (password 1234) are indicated in the asterisk configuration file sip.conf, and queue 1234 is added to the queues.conf file, to which these two subscribers are added. Set up your softphone or hardphone for 101 subscribers and try to make a call to 102. (There are no trunks for connecting to external voip services or settings for any hardware, so we will test dialplan on local calls). Information about the call between subscribers should appear in the asterisk console.
Do subscribers work, do calls go through? Ok, then the asterisk in the docker container works as it should.
Dialplan lua is located in the extensions.lua file. In the asterisk configuration files in the store / etc / asterisk folder there is an example of a working lua dialplan.
The extensions and hints variables must be correctly described in this file (in lua terminology, these are “tables”).
The extensions table contains contexts and corresponding extensions. Everything is like in a traditional dialplan. But each extension is processed by its own function, in which you can already do anything on lua, while interacting with the asterisk through the app and channel tables.
Simplest example
It can be seen that the Dial dialplan application is available through the app, it takes all the same parameters as in the traditional dialplan. Through the app, all dialplan applications are available .
The channel variable gives access to channel variables. So, for example, we get dialstatus.
You can change extensions.lua and then reread extensions.lua with a command in the asterisk CLI module reload pbx_lua.so The asterisk will check the lua syntax, and if everything is ok, then load it for execution - you can test the changes.
What else can you do in the lua dialplan?
For example, it is flexible to handle the dialstatus that will be returned by the Dialplan Dial function. No more inventing these Goto (s - $ {DIALSTATUS}, 1) , now you can write a status check as a human
In the example extensions.lua there is an example of a simple ivr: by calling 200, you will hear an entry from the file / var / menu / demo and you can continue by pressing 1 or 2.
For a person who has written a couple of dozen lines of a traditional dialplan, everything should be familiar here. Plus, the full power of lua and the luarocks packages appears. Obviously, I hope that here in the dialplan you can send SMS, email, put data in the database, take data from the database, and the database can be any: mysql, mongodb, redis, etc., make a command call, initiate another call , make a cool routing of calls on trunks, etc., without forgetting, of course, that this all works within the framework of the asterisk, and it is better to solve all the “difficult” tasks separately.
I suggest:
I hope this article will be useful for a quick start, and you will find one free winter evening and try this way of writing dialplans.
Mistakes? Troubles? Questions? Please write.
In addition, many asterisk users have different levels of training: someone is closer to system administration or even to traditional telephony than to programming. Plus, the specifics of telephony - it is better once again with unfamiliar experiments not to load working systems, but to conduct tests and experiments on your laptop - you have to clutter up the system. In general, there are many reasons to “postpone for later.”
In this article I want to show everyone and everyone who works with an asterisk how, using docker, you can quickly get a taste of flexible lua scripts. And then decide whether to use it further in practice or not. (Who is not interested in reading, but interested in watching and listening - at the end of the text is a 6-minute video with the main points and result.)

Introductory word
As part of my several projects, following the current trend of packaging everything in containers, I prepared an image of astolua (asterisk + lua). The Dockerfile contains commands for installing asterisk 11, lua 5.1, luarocks (package manager for lua), luamongo (driver for accessing mongodb), some lua rocks packages. In the future, you can take only useful for yourself in the docker-astolua repository and collect your workhorse.
Undoubtedly, the advantage of the docker is the ability to download an image, conduct tests, experiments, tests, and then remove the images, leaving your operating system clean and familiar.
Based on the astolua image, we will create our own working image, in which we will use the test configuration files for the asterisk and dialplan on lua.
Training
We need docker. If you don’t have it installed, then please install docker first ( official documentation , article on Habr ).
We also need git ( installing git ). I
also note right away that my working system is Ubuntu 14.04. If you use another Linux, then there shouldn’t be any differences in commands, but nuances are not excluded.
Download astolua image
We tighten the image (attention, the image will be downloaded from the hub.docker.com repository ~ 600MB in size).
docker pull antirek/astolua
Sample
We clone docker-astolua-sample - this is a pre-prepared set of files for this article.
git clone https://github.com/antirek/docker-astolua-sample.git
cd docker-astolua-sample
Now let's stop at sample and see the contents of the directory.
Dockerfile
File A file for building our working image. In it, we indicate that we take astolua as a basis. Then add the startup script after_start.sh, which will be executed when the container starts. The asterisk console log will be displayed in the console where we will launch the container.
Build file
within a file docker team to build the image of our sample Dockerfile.
docker build -t "astolua:sample" .
Run
file Inside the file, the docker command to launch the container based on the sample image with the configuration of the resources it needs.
docker run \
-v /etc/localtime:/etc/localtime:ro \
-v $(pwd)/store/etc/asterisk:/etc/asterisk \
-v $(pwd)/store/var/log/asterisk:/var/log/asterisk \
-v $(pwd)/store/var/menu:/var/menu/ \
--net=host \
-i -t "astolua:sample"
Store
folder The store folder contains the asterisk configuration files (those usually located in / etc / asterisk) and folders for logs and voice menus.
The run command is most interesting because The required resources for the container are indicated here. For example, with the option -v $ (pwd) / store / etc / asterisk: / etc / asterisk we specify that the configuration files from our store folder should be inside the container in their place in / etc / asterisk.
Why are the commands in files? It is convenient to edit commands in files, as this speeds up the time to test changes in teams with different options, and also all changes will fall under version control. And it’s also convenient to transfer options to docker-compose later if the image will be shared with others.
Back to the console.
Let's make an image of astolua: sample (in the directory where we cloned docker-astolua-sample)
./build
We start asterisk (if you already have an asterisk or other service occupying port 5060 already running on the machine, it is better to stop it first)
./run
The asterisk boot log should fall into the console. You can test the connection.
Two subscribers 101 and 102 (password 1234) are indicated in the asterisk configuration file sip.conf, and queue 1234 is added to the queues.conf file, to which these two subscribers are added. Set up your softphone or hardphone for 101 subscribers and try to make a call to 102. (There are no trunks for connecting to external voip services or settings for any hardware, so we will test dialplan on local calls). Information about the call between subscribers should appear in the asterisk console.
Do subscribers work, do calls go through? Ok, then the asterisk in the docker container works as it should.
Dialplan lua
Dialplan lua is located in the extensions.lua file. In the asterisk configuration files in the store / etc / asterisk folder there is an example of a working lua dialplan.
The extensions and hints variables must be correctly described in this file (in lua terminology, these are “tables”).
The extensions table contains contexts and corresponding extensions. Everything is like in a traditional dialplan. But each extension is processed by its own function, in which you can already do anything on lua, while interacting with the asterisk through the app and channel tables.
Simplest example
extensions = {
["internal"] = {
["_1XX"] = function (context, extension)
-- do something --
app.dial('SIP/'..extension);
-- do something again
end;
}
}
It can be seen that the Dial dialplan application is available through the app, it takes all the same parameters as in the traditional dialplan. Through the app, all dialplan applications are available .
The channel variable gives access to channel variables. So, for example, we get dialstatus.
extensions = {
["internal"] = {
["_1XX"] = function (context, extension)
-- do something --
app.dial('SIP/'..extension);
local dialstatus = channel["DIALSTATUS"]:get();
app.noop('dialstatus: '..dialstatus);
end;
}
}
You can change extensions.lua and then reread extensions.lua with a command in the asterisk CLI module reload pbx_lua.so The asterisk will check the lua syntax, and if everything is ok, then load it for execution - you can test the changes.
What else can you do in the lua dialplan?
For example, it is flexible to handle the dialstatus that will be returned by the Dialplan Dial function. No more inventing these Goto (s - $ {DIALSTATUS}, 1) , now you can write a status check as a human
extensions = {
["internal"] = {
["_1XX"] = function (context, extension)
app.dial('SIP/'..extension);
local dialstatus = channel["DIALSTATUS"]:get();
if dialstatus == 'BUSY' then
-- do something
elseif dialstatus == 'CHANUNAVAIL' then
-- do another thing
end;
end;
}
}
In the example extensions.lua there is an example of a simple ivr: by calling 200, you will hear an entry from the file / var / menu / demo and you can continue by pressing 1 or 2.
local ivr = function (context, extension)
app.read("IVR_CHOOSE", "/var/menu/demo", 1, nil, 2, 3);
local choose = channel["IVR_CHOOSE"]:get();
if choose == '1' then
app.queue('1234');
elseif choose == '2' then
dial('internal', '101');
else
app.hangup();
end;
end;
For a person who has written a couple of dozen lines of a traditional dialplan, everything should be familiar here. Plus, the full power of lua and the luarocks packages appears. Obviously, I hope that here in the dialplan you can send SMS, email, put data in the database, take data from the database, and the database can be any: mysql, mongodb, redis, etc., make a command call, initiate another call , make a cool routing of calls on trunks, etc., without forgetting, of course, that this all works within the framework of the asterisk, and it is better to solve all the “difficult” tasks separately.
What's next?
I suggest:
- see the official documentation of Asterisk and LUA - enough examples and comparisons to figure out the basics
- read once more articles on Habré from Sayman_Nsk and ovoshlook : dialplan on lua , AMI in dialplan , update of DEF codes , creation of IVR
- see an example of a lua dialplan from Igmar where most of the PBX functionality is implemented: calls to subscribers, registration / deregistration of queue operators, webhooks for incoming calls, routing depending on the day of the week and time of day.
- "Gash" something of their own :)
I hope this article will be useful for a quick start, and you will find one free winter evening and try this way of writing dialplans.
Mistakes? Troubles? Questions? Please write.