Distributed Device States

Asterisk is primarily designed to run on a single system. However, as requirements for scalability increase, it is common for deployments to require multiple Asterisk servers. Since that has become increasingly common, some features have been added to make it easier to coordinate multiple Asterisk servers. One of those features is distributed device state support.

What this means is that if a device is on a call on one Asterisk server, the state of that device on all servers reflects that. To be more specific, the way this works is that every server knows the state of each device from the perspective of each server. Using this collection of states, each server will calculate what the overall device state value is to report to the rest of Asterisk.

To accomplish distributed device state, some sort of messaging mechanism must be used for the servers to communicate with each other. Two such mechanisms are supported as of Asterisk 1.8: AIS and XMPP.

Using OpenAIS

The Application Interface Specification (AIS) is a standardized set of messaging middleware APIs. The definition for the APIs is provided by the Service Availability Forum. The open source implementation of AIS that was used for the development and testing of this functionality is OpenAIS, which is built on Corosync.

Corosync, and thus OpenAIS, is built in such a way that nodes must be located on the same high-speed, low-latency LAN. If your deployment is geographically distributed, you should use the XMPP-based distributed device state support, which is discussed in the section called “Using XMPP”.

Installation

The first step to getting the necessary components installed is to install Corosync and OpenAIS. Corosync depends on the NSS library. Install the libnss3-dev package on Ubuntu or the nss-devel package on CentOS.

Next, install Corosync and OpenAIS. There may be packages available, but they are also fairly straightforward to install from source. Download the latest releases from the Corosync and OpenAIS home pages. Then, execute the following commands to compile and install each package:

$ tar xvzf corosync-1.2.8.tar.gz
$ cd corosync-1.2.8
$ ./configure
$ make
$ sudo make install

$ tar xvzf openais-1.1.4.tar.gz
$ cd openais-1.1.4
$ ./configure
$ make
$ sudo make install

If you installed Asterisk prior to installing Corosync and OpenAIS, you will need to re-compile and reinstall Asterisk to get AIS support. Start by running the Asterisk configure script. The configure script is responsible for inspecting the system to find out which optional dependencies can be found so that the build system knows which modules can be built:

$ cd /path/to/asterisk
$ ./configure

After running the configure script, run the menuselect tool to ensure that Asterisk has been told to build the res_ais module (this module can be found in the Resource Modules section of menuselect):

$ make menuselect

Finally, compile and install Asterisk:

$ make
$ sudo make install

Note

This is a pretty quick and crude set of instructions for compiling and installing Asterisk. For a much more complete set of instructions, please see Chapter 3, Installing Asterisk.

OpenAIS configuration

Now that OpenAIS has been installed, it needs to be configured. There is a configuration file for both OpenAIS and Corosync that must be put in place. Check to see if /etc/ais/openais.conf and /etc/corosync/corosync.conf exist. If they do not exist, copy in the sample configuration files:

$ sudo mkdir -p /etc/ais
$ cd openais-1.1.4
$ sudo cp conf/openais.conf.sample /etc/ais/openais.conf

$ sudo mkdir -p /etc/corosync
$ cd corosync-1.2.8
$ sudo cp conf/corosync.conf.sample /etc/corosync/corosync.conf

Next, you will need to edit both the openais.conf and corosync.conf files. There are a number of options here, but the most important one that must be changed is the bindnetaddr option in the totem-interface section. This must be set to the IP address of the network interface that this node will use to communicate with the rest of the cluster:

totem {
    ...
    interface {
        ringnumber: 0
        bindnetaddr: 10.24.22.144
        mcastaddr: 226.94.1.1
        mcastport: 5405
    }
}

For detailed documentation on the rest of the options in these configuration files, see the associated manpages:

$ man openais.conf
$ man corosync.conf

To get started with testing out basic OpenAIS connectivity, try starting the aisexec application in the foreground and watching the output:

$ sudo aisexec -f

For example, if you watch the output of aisexec on the first node while you bring up the second node, you should see output that reflects that the cluster now has two connected nodes:

Nov 13 06:55:30 corosync [CLM   ] CLM CONFIGURATION CHANGE
Nov 13 06:55:30 corosync [CLM   ] New Configuration:
Nov 13 06:55:30 corosync [CLM   ]       r(0) ip(10.24.22.144) 
Nov 13 06:55:30 corosync [CLM   ]       r(0) ip(10.24.22.242) 
Nov 13 06:55:30 corosync [CLM   ] Members Left:
Nov 13 06:55:30 corosync [CLM   ] Members Joined:
Nov 13 06:55:30 corosync [CLM   ]       r(0) ip(10.24.22.242) 
Nov 13 06:55:30 corosync [TOTEM ] A processor joined or left the membership and a new 
membership was formed.
Nov 13 06:55:30 corosync [MAIN  ] Completed service synchronization, ready to provide
service.

Tip

If you have any trouble getting the nodes to sync up with each other, one thing to check is that there are no firewall rules on the nodes that are blocking the multicast traffic that is used for the nodes to communicate with each other.

Asterisk configuration

The res_ais module for Asterisk has a single configuration file, /etc/asterisk/ais.conf. One short section is required in this file to enable distributed device state in an AIS cluster. Place the following contents in the /etc/asterisk/ais.conf file:

[device_state]

type = event_channel
publish_event = device_state
subscribe_event = device_state

There is an Asterisk CLI command that can be used to ensure that this configuration has been loaded properly:

*CLI> ais evt show event channels 

=============================================================
=== Event Channels ==========================================
=============================================================
===
=== ---------------------------------------------------------
=== Event Channel Name: device_state
=== ==> Publishing Event Type: device_state
=== ==> Subscribing to Event Type: device_state
=== ---------------------------------------------------------
===
=============================================================

Another useful Asterisk CLI command provided by the res_ais module is used to list the members of the AIS cluster:

*CLI> ais clm show members 

=============================================================
=== Cluster Members =========================================
=============================================================
===
=== ---------------------------------------------------------
=== Node Name: 10.24.22.144
=== ==> ID: 0x9016180a
=== ==> Address: 10.24.22.144
=== ==> Member: Yes
=== ---------------------------------------------------------
===
=== ---------------------------------------------------------
=== Node Name: 10.24.22.242
=== ==> ID: 0xf216180a
=== ==> Address: 10.24.22.242
=== ==> Member: Yes
=== ---------------------------------------------------------
===
=============================================================

Testing device state changes

Now that you’ve set up and configured distributed device state using OpenAIS, there are some simple tests that can be done using custom device states to ensure that device states are being communicated between the servers. Start by creating a test hint in the Asterisk dialplan, /etc/asterisk/extensions.conf:

[devstate_test]

exten => foo,hint,Custom:abc

Now, you can adjust the custom device state from the Asterisk CLI using the dialplan set global CLI command and then check the state on each server using the core show hints command. For example, we can use this command to set the state on one server:

pbx1*CLI> dialplan set global DEVICE_STATE(Custom:abc) INUSE

    -- Global variable 'DEVICE_STATE(Custom:abc)' set to 'INUSE'

and then, check the state on another server using this command:

*CLI> core show hints

-= Registered Asterisk Dial Plan Hints =-
   foo@devstatetest        : Custom:abc        State:InUse       Watchers  0

If you would like to dive deeper into the processing of distributed device state changes, there are some useful debug messages that can be enabled. First, enable debug on the Asterisk console in /etc/asterisk/logger.conf. Then, enable debugging at the Asterisk CLI:

*CLI> core set debug 1

With the debug output enabled, you will see some messages that show how Asterisk is processing each state change. When the state of a device changes on one server, Asterisk checks the state information it has for that device on all servers and determines the overall device state. The following examples illustrate:

*CLI> dialplan set global DEVICE_STATE(Custom:abc) NOT_INUSE

    -- Global variable 'DEVICE_STATE(Custom:abc)' set to 'NOT_INUSE'

[Nov 13 13:27:12] DEBUG[14801]: devicestate.c:652 
handle_devstate_change: Processing device state change for 'Custom:abc'
[Nov 13 13:27:12] DEBUG[14801]: devicestate.c:602 
process_collection: Adding per-server state of 'Not in use' for 'Custom:abc'
[Nov 13 13:27:12] DEBUG[14801]: devicestate.c:602 
process_collection: Adding per-server state of 'Not in use' for 'Custom:abc'
[Nov 13 13:27:12] DEBUG[14801]: devicestate.c:609 
process_collection: Aggregate devstate result is 'Not in use' for 'Custom:abc'
[Nov 13 13:27:12] DEBUG[14801]: devicestate.c:631 
process_collection: Aggregate state for device 'Custom:abc' has changed to 
'Not in use'

*CLI> dialplan set global DEVICE_STATE(Custom:abc) INUSE

    -- Global variable 'DEVICE_STATE(Custom:abc)' set to 'INUSE'

[Nov 13 13:29:30] DEBUG[14801]: devicestate.c:652 handle_devstate_change: 
Processing device state change for 'Custom:abc'
[Nov 13 13:29:30] DEBUG[14801]: devicestate.c:602 process_collection: 
Adding per-server state of 'Not in use' for 'Custom:abc'
[Nov 13 13:29:30] DEBUG[14801]: devicestate.c:602 process_collection: 
Adding per-server state of 'In use' for 'Custom:abc'
[Nov 13 13:29:30] DEBUG[14801]: devicestate.c:609 process_collection: 
Aggregate devstate result is 'In use' for 'Custom:abc'
[Nov 13 13:29:30] DEBUG[14801]: devicestate.c:631 process_collection: 
Aggregate state for device 'Custom:abc' has changed to 'In use'

Using XMPP

The eXtensible Messaging and Presence Protocol (XMPP), formerly (and still commonly) known as Jabber, is an IETF standardized communications protocol. It is most commonly known as an IM protocol, but it can be used for a number of other interesting applications as well. The XMPP Standards Foundation (XSF) works to standardize extensions to the XMPP protocol. One such extension, referred to as PubSub, provides a publish/subscribe mechanism.

Asterisk has the ability to use XMPP PubSub to distribute device state information. One of the nice things about using XMPP to accomplish this is that it works very well for geographically distributed Asterisk servers.

Installation

To distribute device states using XMPP, you will need an XMPP server that supports PubSub. One such server that has been successfully tested against Asterisk is Tigase.

The Tigase website has instructions for installing and configuring the Tigase server. We suggest that you follow those instructions (or the instructions provided for whatever other server you may choose to use) and come back to this book when you’re ready to work on the Asterisk-specific parts.

On the Asterisk side of things, you will need to ensure that you have installed the res_jabber module. You can check to see if it is already loaded at the Asterisk CLI:

*CLI> module show like jabber

Module                         Description                              Use Count 
res_jabber.so                  AJI - Asterisk Jabber Interface          0         
1 modules loaded

If you are using a custom /etc/asterisk/modules.conf file that lists only specific modules to be loaded, you can also check the filesystem to see if the module was compiled and installed:

$ ls -l /usr/lib/asterisk/modules/res_jabber.so

-rwxr-xr-x 1 root root 837436 2010-11-12 15:33 /usr/lib/asterisk/modules/res_jabber.so

If you do not yet have res_jabber installed, you will need to install the iksemel and OpenSSL libraries. Then, you will need to recompile and reinstall Asterisk. Start by running the Asterisk configure script, which is responsible for inspecting the system and locating optional dependencies, so that the build system knows which modules can be built:

$ cd /path/to/asterisk
$ ./configure

After running the configure script, run the menuselect tool to ensure that Asterisk has been told to build the res_jabber module. This module can be found in the Resource Modules section of menuselect:

$ make menuselect

Finally, compile and install Asterisk:

$ make
$ sudo make install

Note

This is a pretty quick and crude set of instructions for compiling and installing Asterisk. For a much more complete set of instructions, please see Chapter 3, Installing Asterisk.

Creating XMPP accounts

Unfortunately, Asterisk is currently not able to register new accounts on an XMPP server. You will have to create an account for each server via some other mechanism. The method we used while testing was to use an XMPP client such as Pidgin to complete the account registration process. After account registration is complete, the XMPP client is no longer needed. For the rest of the examples, we will use the following two buddies, both of which are on the server jabber.shifteight.org:

  • server1@jabber.shifteight.org/astvoip1

  • server2@jabber.shifteight.org/astvoip2

Asterisk configuration

The /etc/asterisk/jabber.conf file will need to be configured on each server. We will show the configuration for a two-server setup here, but the configuration can easily be expanded to more servers as needed. Example 14.2, “jabber.conf for server1” shows the contents of the configuration file for server 1 and Example 14.3, “jabber.conf for server2” shows the contents of the configuration file for server 2. For additional information on the jabber.conf options associated with distributed device states, see the configs/jabber.conf.sample file that is included in the Asterisk source tree.

Example 14.2. jabber.conf for server1

[general]
autoregister = yes

[asterisk]
type = client
serverhost = jabber.shifteight.org
pubsub_node = pubsub.jabber.shifteight.org
username = server1@jabber.shifteight.org/astvoip1
secret = mypassword
distribute_events = yes
status = available
usetls = no
usesasl = yes
buddy = server2@jabber.shifteight.org/astvoip2

Example 14.3. jabber.conf for server2

[general]
autoregister = yes

[asterisk]
type = client
serverhost = jabber.shifteight.org
pubsub_node = pubsub.jabber.shifteight.org
username = server2@jabber.shifteight.org/astvoip2
secret = mypassword
distribute_events = yes
status = available
usetls = no
usesasl = yes
buddy = server1@jabber.shifteight.org/astvoip1

Testing

To ensure that everything is working properly, start by doing some verification of the jabber.conf settings on each server. There are a couple of relevant Asterisk CLI commands that can be used here. The first is the jabber show connected command, which will verify that Asterisk has successfully logged in with an account on the jabber server. The output of this command on the first server shows:

*CLI> jabber show connected

Jabber Users and their status:
       User: server1@jabber.shifteight.org/astvoip1     - Connected
----
   Number of users: 1

Meanwhile, if jabber show connected is executed on the second server, it shows:

*CLI> jabber show connected

Jabber Users and their status:
       User: server2@jabber.shifteight.org/astvoip2     - Connected
----
   Number of users: 1

The next useful command for verifying the setup is jabber show buddies. This command allows you to verify that the other server is correctly listed on your buddy list. It also lets you see if the other server is seen as currently connected. If you were to run this command on the first server without Asterisk currently running on the second server, the output would look like this:

*CLI> jabber show buddies

Jabber buddy lists
Client: server1@jabber.shifteight.org/astvoip1
    Buddy: server2@jabber.shifteight.org
           Resource: None
    Buddy: server2@jabber.shifteight.org/astvoip2
           Resource: None

Next, start Asterisk on the second server and run jabber show buddies on that server. The output will contain more information, since the second server will see the first server online:

*CLI> jabber show buddies

Jabber buddy lists
Client: server2@jabber.shifteight.org/astvoip2
    Buddy: server1@jabber.shifteight.org
           Resource: astvoip1
               node: http://www.asterisk.org/xmpp/client/caps
               version: asterisk-xmpp
               Jingle capable: yes
           Status: 1
           Priority: 0
    Buddy: server1@jabber.shifteight.org/astvoip1
           Resource: None

At this point, you should be ready to test out the distribution of device states. The procedure is the same as that for testing device states over AIS, which can be found in the section called “Testing device state changes”.