Book Image

Asterisk 1.4 - the Professional's Guide

Book Image

Asterisk 1.4 - the Professional's Guide

Overview of this book

Asterisk is the leading Open Source Telephony application and PBX software solution. It represents an effective, easy-to-administer, and accessible platform for running enterprise telephony requirements. The real world, however, offers numerous hurdles when running Asterisk in the commercial environment including call routing, resilience, or integrating Asterisk with other systems. This book will show you some of the ways to overcome these problems. As the follow-up to Packt's highly successful 2005 title Building Telephony Systems with Asterisk, this book presents the collected wisdom of Asterisk Professionals in the commercial environment. Aimed at Administrators and Asterisk Consultants who are comfortable with the basics of Asterisk operation and installation, this book covers numerous hands-on topics such as Call Routing, Network Considerations, Scalability, and Resilience ñ all the while providing practical solutions and suggestions. It also covers more business-related areas like Billing Solutions and a Winning Sales Technique. Even if your interest or experience with Asterisk is lower level, this book will provide a deeper understanding of how Asterisk operates in the real world. Asterisk is deployed across countless enterprises globally. Running on Linux, it has constantly demonstrated its resilience, stability, and scalability and is now the advanced communication solution of choice to many organizations and consultants. With a foreword from Mark Spencer, the man behind Asterisk, this book presents the accumulated wisdom of three leading Asterisk Consultants and shows the reader how to get the most out of Asterisk in the commercial environment. Over the course of eleven chapters, this book introduces the reader to topics as diverse as Advanced Dial Plans, Network Considerations, and Call Routing, through to Localization, DAHDI, Speech Technology, and Working with a GUI. The book also covers the more nebulous aspects of being an Asterisk professional such as evaluating customer requirements and pitching for contracts. This book represents the wisdom and thoughts of front line consultants. The knowledge they impart will prove informative, thought provoking and be of lasting interest to Asterisk professionals.
Table of Contents (20 chapters)
Asterisk 1.4
Credits
Foreword
About the Authors
About the Reviewers
Preface
9
Interfacing with Traditional Analog and Digital Telephony
Sample Appointment Sheet

Dialplan features and additions


In this section, we are going to look at the DEVSTATE() function and the System() application. We will see how we can check and change the "status" of devices with the DEVSTATE() function and use the system application to cause scripts on the server to be run.

func_devstate

The func_devstate application allows the status of a peer to be known before you dial it. This is very useful in many applications. We will cover a few of them here but you will be able to find many more.

The func_devstate application is part of Asterisk 1.6, but Russell Bryant (of Digium) has a back-ported version for Asterisk 1.4. This can now be found at:

http://svn.digium.com/community/russell/asterisk-1.4/func_devstate-1.4/func_devstate.c

For most Linux distributions, installing the function is pretty simple:

cd /usr/src/asterisk/funcs
wget http://svn.digium.com/community/russell/asterisk-1.4/func_devstate-1.4/func_devstate.c
cd ..
make clean
./configure
make menuselect
Choose option -> 6. Dialplan Functions
Then make sure that you have an entry like 8.func_devstate
make
make install

Note

If under Dialplan Functions, the DEVSTATE() function does not show up, you will need to edit the menuselect-tree to add it.

<member name="func_devstate" displayname="Gets or sets a device state in the dialplan" remove_on_change="funcs/func_devstate.o funcs/func_devstate.so">

Then compile Asterisk as shown previously.

What can we use the DEVSTATE() function for?

The DEVSTATE() function is versatile, allowing us to check and/or set the status of a device, as its name suggests. One very common use is to activate phone lamps, showing users if they have set a feature such as DND or call forwarding. In the following examples, we will look at both setting and checking methods:

The function reports on, or can set, the following states:

NOT_INUSE
INUSE
BUSY
INVALID
UNAVAILABLE
RINGING
RINGINUSE
ONHOLD
Outgoing trunk selection

The application can be used here to check that an outgoing peer is "available" and not "down", before you send a call to it. This is useful if you have peers or remote systems that are on variable quality connections.

exten => _90.,1,Macro(outdial,${PRIDIAL},${INT_CALL_ID},${INT_CALL_LIMIT},${BAKDIAL},${PRIPEER})
[macro-outdial]
exten => s,1, Set(GROUP()=OUTBOUND_GROUP) ;Set Group
exten => s,2,GotoIf($["${DEVSTATE(${ARG5})}"="UNAVALIABLE"]?s,7)
exten => s,3, GotoIf($[${GROUP_COUNT(OUTBOUND_GROUP)} > ${ARG3}]?103)
exten => s,4, Set(CALLERID(num)=${ARG2})
exten => s,5, Dial(${ARG1}${MACRO_EXTEN:1},180)
exten => s,6, Hangup()
exten => s,7, Dial(${ARG4}${MACRO_EXTEN:1},180)
exten => s,8, Hangup()
exten => s,n, Noop(call Limit exceeded)

This example is an expansion of our previously used macro and has a couple of extra arguments passed to it. This makes it very flexible, as the backup peer can be different for each dialed number.

Calling extensions

The application lets you see if extensions are busy or "out of service" before calling them. This can be useful for handsets that support call waiting, but you don't want to fully disable it for all calls. Before calling the extension, you can check to see if the extension has call waiting enabled and then, depending on the result, check the device status as follows:

exten => 2XXX,1,Macro(dialext)
[Macro-dialext]
exten => s,1,NoOp(SIP/${MACRO_EXTEN} has state ${DEVSTATE(SIP/$ {MACRO_EXTEN})})
exten => s,n,Set(CW=${DB(CW/${MACRO_EXTEN})})
exten => s,n,GotoIf($["${CW}"="YES"]?dial)
exten => s,n,GotoIf($["${DEVSTATE(SIP/${MACRO_EXTEN})}"!="NOT_INUSE"]? s-BUSY,1)
exten => s,n(dial),Dial(SIP/${MACRO_EXTEN},35)
exten => s,n,Goto(s-BUSY,1)
exten => s-BUSY,1,Voicemail(${MACRO_EXTEN},b)
exten => s-BUSY,n,Hangup()

In the previous example, we have used the internal database to set the flag to say if call waiting is enabled or not. If call waiting is anything other than YES, the status of the extension will be checked, otherwise the DEVSTATE isn't checked and the extension is just called. As we will see next, we can expand this to light a BLF (Busy Lamp Field) key as well, to give a visual indication to users of the device status.

Setting lights

We can also use the DEVSTATE() function to set BLF lights on and off, a very simple but highly effective feature. This is particularly helpful if you are using the dialplan for setting call forwards or DND. It can also show if a call center agent is logged in or not, on their phone.

To illustrate this functionality, we have a very simple example showing how to turn the light on and off. It uses one number to toggle the light status and is not specific for the particular phone all phones dial the same number and it is the CHANNEL variable, which is used to set it for a specific phone. In this example, we have two hints 4078 and 4071, and these are linked to extensions 5078 and 5071.

Using this code and adding additional code to set the database key for call waiting (as we have already covered) would give the phone user a visual indication as to whether call waiting is set or not.

exten => 4071,hint,Custom:light5071
exten => 4078,hint,Custom:light5078
exten => 1236,1,Goto(1236-${DEVSTATE(Custom:light${CHANNEL:4:4})}|1)
exten => 1236-UNKNOWN,1,Goto(1236-NOT_INUSE|1)
exten => 1236-NOT_INUSE,1,Noop(Turn ${CHANNEL:4:4} light on)
exten => 1236-NOT_INUSE,n,Set(DEVSTATE(Custom:light${CHANNEL:4:4})= INUSE)
exten => 1236-INUSE,1,Noop(Turn ${CHANNEL:4:4} light off)
exten => 1236-INUSE,n,Set(DEVSTATE(Custom:light{CHANNEL:4:4})= NOT_INUSE)

By using userevent, you can also send out manager events to update the Flash Operator Panel. The following would set the CW flag for the Flash Operator Panel for our extension and change the icon to reflect the status.

exten => 1236-NOT_INUSE,2,UserEvent(ASTDB|Channel: ${CHANNEL}^Family: CW^Value: SET ^)

Note

There is also a version of DEVSTATE() called EXTSTATE(). It is a modified version of the DEVSTATE() function that returns the state of an extension, rather than the state of a device. This means you can write dialplan logic based on the state of an extension (in use, ringing, on hold, and so on). The extension just needs to have a hint so we can determine which devices to check.

Boosting outgoing call capacity

We're going to have a look at how DEVSTATE() has been used to address an unusual situation. A call-centre customer wished to temporarily increase their outgoing call capacity, in this case by 20 concurrent calls, to cater for a particular project. However, in their location, with their budget and given the temporary need for extra capacity, the only effective means of boosting bandwidth is to utilize multiple ADSL circuits. In other words, SDSL and leased line circuits were too costly for consideration. Therefore, there was a need to bond multiple ADSL circuits together within Asterisk, in order to provide a single high-bandwidth circuit for outbound calls.

It may seem obvious, but when calculating call capacity with ADSL circuits, the figure we're interested in is the lower of the upload/download speeds. It doesn't matter if you have a superfast 20 MB DSL circuit, chances are that you only have an uplink speed of 800 kbps or less. It must also be remembered that once traffic exceeds 50% of the link speed, collisions and latency are likely to become an issue and must be addressed. This gives you a theoretical limit of up to 10 uncompressed calls per circuit if you're really lucky. Of course, with the GSM codec you can get a lot more, but at the cost of audio quality, which your customer is unlikely to accept. They expect PSTN quality and nothing less. If bandwidth utilization is on the borderline with an uncompressed codec, it can be advantageous to use a commercial (non-free) codec such as G729, which is obtainable from Digium.

Using multiple broadband lines

Using the criteria already discussed and assuming a 500 kbps uplink speed, it was determined that four broadband circuits were needed. This may sound expensive, but in reality it's not, when you consider that the alternative was 20 channels of a PRI (ISDN 30), which worked out at twice the cost of four PSTN lines with broadband. As we'll see later in the sales appendix, a major benefit of VoIP is that the customer is paying much less for line rental. This solution only reinforces that benefit.

We are going to describe a solution that used four broadband circuits, but another advantage of this approach is that it is very scalable. To illustrate, a system has been set up for a charity in the UK that had 75 agents placing thousands of calls a day on just eight broadband circuits.

Note

This example was tested and proven using Asterisk 1.4.19.2, and should work with releases up to 1.4.19.2 - it’s operation cannot be guaranteed in other versions.

Configuration overview

Once the circuits are delivered, you will end up with four routers connected to the broadband service of your choice. Each router will have a unique IP address. In our case, we shall assume they are as follows:

192.168.1.1
192.168.1.2
192.168.1.3
192.168.1.4

We will also assume that the VoIP ITSP has multiple IP addresses that you can connect to, though if not, you can probably do some clever address translation in the routers.

Setting up the routing in Linux

Let's assume our VoIP ISP has provided us with the following external IP addresses:

88.88.88.81
88.88.88.82
88.88.88.83
88.88.88.84

Within Linux, you can easily set up different gateway addresses for a given destination. The file that manages the gateways is normally called ifup-routes in the /etc/sysconfig/network-scripts directory.

To configure the gateways, we append the following to the ifip-routes file:

/sbin/route add 88.88.88.81 gw 192.168.1.1

/sbin/route add 88.88.88.82 gw 192.168.1.2

/sbin/route add 88.88.88.83 gw 192.168.1.3

/sbin/route add 88.88.88.84 gw 192.168.1.4

Taking the last entry, what we're saying is that for all traffic to 88.88.88.84, route it via the router at 192.168.1.4.

If you reboot and run the command route n in a terminal session, you'll see these routes in place.

Configuring Asterisk

We now turn our attention to the Asterisk configuration. When we make a call, we're going to keep count of how many calls we have on a broadband line, so that when the circuit is "full", we can move on to the next available one.

Firstly, in the extensions.conf file, we need to declare a variable that sets the maximum concurrent calls we will allow through any one router.

MAXVOIPCALLS=5 ; Maximum Calls we allow over IP (outbound)

We've previously set up four entries in the iax.conf file called iaxline1 through to iaxline4. They have identical entries, with the exception on the host= line. Here we assign the appropriate external IP address, that is, 88.88.88.81 for iaxline1 and so on.

Now, we need to declare the IAX channels as follows:

AIXVOIPOUT = IAX2/iaxline1
AIXVOIPOUT1 = IAX2/iaxline2

As expected, we use a macro to manage the call routing. The example below only shows two lines for the sake of brevity.

[macro-voiptrunk]
exten => s,1,Noop(Number of Broadband calls)
;Use devstate to test the availability of the trunks. You could put some code in, to use an alternative if they are off line
exten => s,2,Noop(Trunk 1 ${DEVSTATE(${AIXVOIPOUT})} -> ${GROUP_COUNT(VOIPTRUNKS@list1)})
exten => s,3,Noop(Trunk 2 ${DEVSTATE(${AIXVOIPOUT1})} -> ${GROUP_COUNT(VOIPTRUNKS@list2)})
exten => s,4,GoToIf($[${GROUP_COUNT(VOIPTRUNKS@list1)} < ${MAXVOIPCALLS}]?10:20)
;Have we exceeded the max calls per trunk? If so, jump to Extension 20 and use second trunk
exten => s,10,gotoif($[${DEVSTATE(${AIXVOIPOUT})} = UNAVAILABLE]?20:11)
;Here we test to see if the trunk is available, if it's gone off-line, we use the second trunk
exten => s,11,Set(GROUP(list1)=VOIPTRUNKS) ;increment the usage count.
exten => s,12,noop(${DEVSTATE(${AIXVOIPOUT})})
exten => s,13,Noop(Trunk 1-> ${GROUP_COUNT(VOIPTRUNKS@list1)})
exten => s,14,Dial(${AIXVOIPOUT}/${MACRO_EXTEN})
exten => s,15,Goto(s-${DIALSTATUS},1)
exten => s,20,GoToIf($[${GROUP_COUNT(VOIPTRUNKS@list2)} < ${MAXVOIPCALLS}]?21:40)
exten => s,21,Set(GROUP(list2)=VOIPTRUNKS)
exten => s,22,Noop(Number of Broadband calls)
exten => s,23,Noop(Trunk 2-> ${GROUP_COUNT(VOIPTRUNKS@list2)})
exten => s,24,Dial(${AIXVOIPOUT1}/${MACRO_EXTEN})
exten => s,25,Goto(s-${DIALSTATUS},1)
exten => s,40,Congestion(15) ; No more lines left
Explanation of the macro

Priorities "2" and "3" use the DEVSTATE() function to test the availability of the broadband lines. If a line is down, UNAVAILABLE will be returned. At "4", we look to see if we've exceeded max calls on this line. If we haven't, we'll place a call on the first router, otherwise go to the next. "11" records the in-use count and increments it for the given router (list1).

What happens, overall, is that the first 5 calls (set by the global MAXVOIPCALLS) will go via router 1, the sixth will go via router 2. If in the meantime a call is dropped from router 1, the next call placed will go back to router 1, even if other calls are ongoing on router 2.

Finally, we need to add a call to the macro in our dialplan:

[outbound-national]
exten => _0Z.,1,NoOp(national call)
exten => _0Z.,2,Macro(voiptrunk)

The above technique is scalable. You can add as many broadband lines as you need. The end result is that you can say to your customer, "want more outgoing capacity? Just add another DSL line". However, it must not be forgotten that there may be more stable solutions such as SDSL and leased lines, depending on location.

Downsides

The above example works really well for outbound calling, but not so well for inbound. If you own the server the customer is connecting to, then you can reverse the logic at your end. If you don't, then all you can do is allocate one router for inbound (register via an inbound router) and the rest for outbound.

System() application

The System() application allows Asterisk to run Linux commands and shell scripts. What we will look at here is a simple hotdesking deployment script for asterisk. This type of deployment method is used by all commercial PBXs and is needed for any enterprise deployment of Asterisk. Hand editing filenames or even configuring phones via their web GUI will not be accepted by a customer or end user.

The dialplan is very simple. The user dials a code from his/her handset and is asked to enter a four-digit number (their hotdesk ID). The dialplan then stores this as a variable. It also sets the caller ID and the IP address for the set and passes these to the script.

[hotdesk_in]
exten => s,1,Answer
exten => s,n,Playback(privacy-thankyou)
exten => s,n,Read(MY_EXTEN,access-code,4)
exten => h,1,Set(MY_IP=${SIPPEER(${CALLERID(num)}:ip)})
exten => h,2,system(/usr/local/sbin/exten_in ${MY_EXTEN} ${CALLERID(num)} ${MY_IP})

With these three variables, the script then knows the handset's existing number, IP address, and the number it wants to be.

In addition, the script then performs an Address Resolution Protocol (ARP) lookup on the IP address to find the phone's MAC address. It needs this because, as in the example, we are using the phones config file in the format of<MAC-ADDRESS>.cfg, and we configure the sets via TFTP (Trivial File Transfer Protocol). Hence, as we know the MAC address we can copy the config files to the correct name.

Firstly, we will copy the old<MAC-ADDRESS>.cfg to a different file name. Then we copy the config file for the extension number we wish the phone to be using the MY_EXTEN variable we have passed to the script to define it to our new<MAC-ADDRESS>.cfg. Now when the set reboots, it will pick up the new file. However, we want this to be automatic and with as many handsets as it can have. The sip notify command does so when configured in the sip_notify.conf file, in the case of Aastra handsets, as follows:

[aastra-check-cfg]
Event=>check-sync
Content-Length=>0

The following command will cause the phone to check the config file for changes and reboot if any change is found:

/usr/sbin/asterisk -rx "sip notify aastra-check-cfg ${CALLERID}"

When it reboots, it will pick up its new configuration. By using scripts such as the previous one, you can speed up "moves, adds, and changes" and cut out the need for engineers to put out or replace handsets. It can also be used to provide a form of hotdesking with the user dialing a code to set the handset as theirs, and then log out when they leave (copying back the previous config ), thus returning the phone to its previous state.