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

Extensions and contexts


Being familiar with Asterisk, you will have a good working understanding of extensions and contexts already. They are, of course, the very heartbeat of Asterisk, and as such they are probably subject to the most change from version to version, as Asterisk evolves to cater for new hardware, software, and more complex working practices. So let's have a quick review of extensions and contexts, pointing out significant changes in versions 1.4 and 1.6, before we proceed to the more advanced techniques and uses.

Pattern matching

Within the dialplan, matching can be either direct or partial against a pattern. Normally in a PBX, these patterns are numeric. But with Asterisk, they can also be alphanumeric or even just alpha. For example 2000, DID01234123456, and Main_number are all valid extensions. As very few phones contain alphabetic keys, the last two are typically only used for incoming DID channels. For the majority of this chapter, we will stick to numeric patterns.

Let's start to explore pattern matching by looking at an extremely simple dialplan:

[context_1]
exten => 123,1,Answer()
exten => 123,n,SayDigits(999${CALLERID(num)})
exten => 123,n,Hangup()

In this dialplan, when a user with a context of context_1 dials 123, they will hear 999 and their caller ID will be read back to them.

Now let's look at a slightly more complex context:

[context_1]
exten => _1X.,1,Answer()
exten => _1X.,n,SayDigits(${EXTEN}${CALLERID(num)})
exten => _1X.,n,Hangup()
exten => 123,1,Answer()
exten => 123,n,SayDigits(123${CALLERID(num)})
exten => 123,n,Hangup()

You might expect that 123 would match against the _1X. extension, as that appears first in the context. However, the way Asterisk orders the dialplan when loading means that exact matches are checked for before pattern matches. Hence if you dial 123, it matches against the 123 pattern first and not the _1X. pattern. This pattern would only route the call if an exact match did not exist in the context.

Note

It is sensible not to use the pattern _. as a catch-all pattern, as this will catch the Asterisk special extensions like i, t, h as well. It is far better to use the _X pattern.

Once understood, pattern matching is pretty straightforward and does what we expect. However, if you introduce included contexts into the mix, things may work in a way you did not expect and the order needs to be thought through carefully. In particular, it's crucial to understand that Asterisk only checks included contexts after checking for exact matches and pattern matches in the local context. The following example illustrates this:

[context_1]
include => context_2
exten => _1X.,1,Answer()
exten => _1X.,n,SayDigits(${EXTEN}${CALLERID(num)})
exten => _1X.,n,Hangup()
include => context_3
exten => 123,1,Answer()
exten => 123,n,SayDigits(123${CALLERID(num)})
exten => 123,n,Hangup()

The above dialplan is sorted internally by Asterisk shown as follows, and you can see that though the included contexts are at the top and in the middle, the local context is read first, then the included contexts are read in the order that they were added. Hence, in this case, a dial string of 122 would be matched by the _1X. pattern before the included contexts are searched.

'123' => 1. Answer()
2. SayDigits(123${CALLERID(num)})
3. Hangup()
'_1X.' => 1. Answer()
2. SayDigits(${EXTEN}${CALLERID(num)})
3. Hangup()
Include => 'context_2'
Include => 'context_3'

Note

If you have a catch-all pattern in your dialplan, consider putting it into a separate context. You can then use the include directive to append that context to the end of the active context, thus ensuring that all of the other pattern matching is attempted first.

One of the most powerful tools you will use on the Asterisk command line is dialplan show <exten>@<context>. For example:

dialplan show 122@context_1

This will show you the matching order that Asterisk will use for the given extension in the specified context, and if there are matches in any included contexts, those contexts will be explicitly identified.

Finally, in a context you may have a switch statement, which includes the dialplan of an external system into the local dialplan. In essence, it's an include for remote systems. Though typing dialplan show will always show the switch statement at the bottom, the defined context on the remote system is searched after the local context on your system and before any local included contexts! So again, you have to be very careful as to what is the context on the remote system as this will be searched before your included contexts.

The syntax of the switch state is as follows:

switch =>IAX2/user:[key]@server/context

The user and key are defined in the called server's iax.conf file, and the context is, of course, in the server's dialplan.

Why use contexts?

In our examples so far we could have achieved the desired results very easily without the use of multiple contexts. The simple functionality we have looked at could be carried out in a single, all-encompassing context. In practice, this approach could be applicable for systems with a very limited number of users and trunks, and with very restricted functionality, as there may not be a need to restrict the calling habits of a subset of users.

Use of contexts becomes desirable when we need to offer different options to different users. This is likely to be most applicable in medium and large companies, where you may have "users" ranging from the CEO down to an emergency phone in a lift. However, it can also be the case in smaller companies, where you might want to restrict home workers from making international calls for instance. When you get many different types of users, writing a distinct dialplan for each becomes problematic. The sheer size and complexity of the dialplan will make code management very complicated.

To simplify things, we first need to think about what makes the dialplan for each extension different. Then we need to think about what remains the same for each extension, as this needs to be made to work as well. What we often find is that most of these differences can be stored and called in two main ways:

  • The user's context

  • Variables linked to that user

We will come to variables shortly, but the grouping of extensions into contexts allows us to separate concise and distinct functions from each other. In doing so, we can control very tightly which contexts are used in each scenario, and also implement one "master" copy of each distinct function, aiding maintenance of the code.

Call barring made simple

To illustrate, let's expand our context a bit and use call barring as an example. We will initially have three levels for this example local, national, and international.

These are defined as follows:

  • Any number starting with a 1-9 is local

  • Anything starting with a 00 is international.

  • Anything else starting with a 0 is national or a mobile number.

This is a simplified example, and uses the UK format of dial prefixes.

We have in this example three contexts local_num, national_num and international_num. These would correspond to the levels of access we have decided on for our users. For example, an executive phone would be allowed access to all numbers whereas a phone on the shop floor may only be allowed access to local numbers.

We will create the three contexts shown as follows. All we are doing in our example is reading back 1, 2, or 3 to indicate the pattern that has been matched followed by the number dialed ${EXTEN}.

[local_num]
Exten => _Z.,1,Answer()
Exten => _Z.,n,SayDigits(1${EXTEN})
Exten => _Z.,n,Hangup()
;
[national_num]
Exten => _0Z.,1,Answer()
Exten => _0Z.,n,SayDigits(2${EXTEN})
Exten => _0Z.,n,Hangup()
;
[international_num]
Exten => _00X.,1,Answer()
Exten => _00X.,n,SayDigits(3${EXTEN})
Exten => _00X.,n,Hangup()

For each context we could write an ordered list to cover all patterns, but it is much neater to create a master context for each user. For example:

[local]
Include => local_num
[national]
Include => national_num
Include => local_num
[international]
Include => international_num
Include => national_num
Include => local_num

Therefore, in the previous example, a user with the national context can dial a normal national number, but not an international number. A user with the international context has the ability to dial both numbers.

This is a pretty simple example with just three level of access, but the modular nature due to the use of contexts allows us to expand it very quickly and easily. For example, we have a user 1000 (our CEO) and he can dial internationally. We also have 1098 and 1099, which are users on the shop floor, and can dial reception and the emergency services.

In this example, we give our CEO a context of [supauser],while the shop floor has a context of [emergencyuser].

The [supauser] context has to be able to dial everything, so it looks like this:

[supauser]
include => premium_num ; allows dialing to premium rate numbers
include => international_num ; allows international dialing
include => national_num ; allows national calls
include => mobile_num ; allows calls to mobile phones
include => local_num ; allows local rate calls
include => free_num ; allows free calls such as 800 or operator services
include => internal_num ; allows the calling of extensions
include => emergency ; allows calls to the emergency services
include => default ; allows access to system features

The shop floor just has the following context:

[emergencyuser]
include => emergency ; allows calls to emergency services reception.

As you can see, we can mix and match these contexts to cover many different types of extensions. Although you may be asking, "Will this really save me time?" well, let's look at two examples. Firstly, our supplier reduces the cost of UK 0870 numbers to free in the evenings as has happened in the UK with BT(British Telecom). Secondly, we also want the shop floor phone (1099) to be able to dial extensions and toll free calls, but not change the dialplan for 1098.

We will deal with the simplest of these extensions (1099) first. All we need to do is change the context associated with this user to a new context called [freeuser]:

[freeuser]
include => free_num ; allows calls to free numbers
include => internal_num ; allows the calling of extensions
include => emergency ; allows calls to the emergency services
include => default ; allows access to system features .

This is a fast and easy change, which will have no effect on other shop floor users.

And to the change to 0870 numbers, this once again can be put into effect very simply. The only change is that evening and weekend calls are now free. Therefore, we could put it into a [free] context. Although, it isn't always free. It is free only at weekends which would not be suitable. Hence, for this we use the GotoIfTime application, which sets the context, extension, and priority in the channel based on the system time, day, date, and month supplied by the OS.

By adding the following to the free context, users can now dial 0870 numbers at the defined times.

exten => _0870XXXXXXX,1,GotoIfTime(17:59-08:00,mon-fri,*,*?national, ${EXTEN},1)
exten => _0870XXXXXXX,1,GotoIfTime(*,sat-sun,*,*?national,${EXTEN},1)

In this case, we have made a change for all users who also have a context allowing both local and free calls (as their context includes the free context).

Time and day call routing

The GotoIfTime() application can introduce some powerful functionality into your dialplan if used properly. An example that follows is for a support company where calls are routed to the call centre or staff member on call at a specific time. The customer had centers round the globe and we routed the calls to whichever center was open at that time of day.

[folthesun]
;
;This section sets the constants and variables for numbers and times
;Nine timezones are defined to allow for 4 a day and sat and sun working
;At present there are 6 destinations for NA AU and EMEA
;
exten => s,1,set(__tzone1=00:00-07:59)
exten => s,n,set(__tzone2=08:00-17:30)
exten => s,n,set(__tzone3=17:31-23:59)
exten => s,n,set(__tzone4=17:31-23:59)
exten => s,n,set(__tzone5=00:00-23:59)
exten => s,n,set(__tzone6=00:00-23:59)
exten => s,n,set(__tzone7=00:00-23:59)
exten => s,n,set(__tzone8=00:00-23:59)
exten => s,n,set(__tzone9=00:00-23:59)
;
exten => s,n,set(_dest1=01234123456) ;dest1 emea_pager
exten => s,n,set(_dest2=001765412345) ;dest2 na_pager
exten => s,n,set(_dest3=006165453457) ;dest3 au_pager
exten => s,n,set(_dest4=08441231234) ;dest4 uk_no
exten => s,n,set(_dest5=001744519651) ;dest5 na_no
exten => s,n,set(_dest6=006118954654) ;dest6 au_no
;
exten => s,n,set(dialpre=9) ;dialing prefix
;
exten => s,n,set(dialcon=international) ;dialing context
;
exten => s,n,Goto(ftstimeing,s,1)
;
[ftstimeing]
;
;This sections runs though the days of the week and checks the time
;against DOW and time
;
exten => s,1,GotoIfTime(${tzone1}|mon|*|*?dest1,1)
exten => s,n,GotoIfTime(${tzone2}|mon|*|*?dest4,1)
exten => s,n,GotoIfTime(${tzone3}|mon|*|*?dest5,1)
exten => s,n,GotoIfTime(${tzone4}|mon|*|*?dest5,1)
exten => s,n,GotoIfTime(${tzone1}|tue|*|*?dest1,1)
exten => s,n,GotoIfTime(${tzone2}|tue|*|*?dest4,1)
exten => s,n,GotoIfTime(${tzone3}|tue|*|*?dest5,1)
exten => s,n,GotoIfTime(${tzone4}|tue|*|*?dest5,1)
exten => s,n,GotoIfTime(${tzone1}|wed|*|*?dest1,1)
exten => s,n,GotoIfTime(${tzone2}|wed|*|*?dest4,1)
exten => s,n,GotoIfTime(${tzone3}|wed|*|*?dest5,1)
exten => s,n,GotoIfTime(${tzone4}|wed|*|*?dest5,1)
exten => s,n,GotoIfTime(${tzone1}|thu|*|*?dest1,1)
exten => s,n,GotoIfTime(${tzone2}|thu|*|*?dest4,1)
exten => s,n,GotoIfTime(${tzone3}|thu|*|*?dest5,1)
exten => s,n,GotoIfTime(${tzone4}|thu|*|*?dest5,1)
exten => s,n,GotoIfTime(${tzone1}|fri|*|*?dest1,1)
exten => s,n,GotoIfTime(${tzone2}|fri|*|*?dest4,1)
exten => s,n,GotoIfTime(${tzone3}|fri|*|*?dest5,1)
exten => s,n,GotoIfTime(${tzone4}|fri|*|*?dest5,1)
exten => s,n,GotoIfTime(${tzone5}|sat|*|*?dest1,1)
exten => s,n,GotoIfTime(${tzone6}|sun|*|*?dest1,1)
;
;Fall through point
exten => s,n,Goto(dest1,1)
contextGotoIfTime() application, using;
;Dialed using the Local channel so call handling is observered
;
exten => dest1,1,Noop(Calling ${dest1})
exten => dest1,n,Dial(Local/${dialpre}${dest1}@${dialcon})
exten => dest1,n,Hangup()
exten => dest2,1,Noop(Calling ${dest2})
exten => dest2,n,Dial(Local/${dialpre}${dest2}@${dialcon})
exten => dest2,n,Hangup()
exten => dest3,1,Noop(Calling ${dest3})
exten => dest3,n,Dial(Local/${dialpre}${dest3}@${dialcon})
exten => dest3,n,Hangup()
exten => dest4,1,Noop(Calling ${dest4})
exten => dest4,n,Dial(Local/${dialpre}${dest4}@${dialcon})
exten => dest4,n,Hangup()
exten => dest5,1,Noop(Calling ${dest5})
exten => dest5,n,Dial(Local/${dialpre}${dest5}@${dialcon})
exten => dest5,n,Hangup()
exten => dest6,1,Noop(Calling ${dest6})
exten => dest6,n,Dial(Local/${dialpre}${dest6}@${dialcon})
exten => dest6,n,Hangup()
exten => i,1,Hangup()
exten => t,1,Hangup()
exten => h,1,Hangup()

This can be expanded to include public holidays, if required. It can be possible to handle many years' public holidays in one line. For example, between the years 2009 and 2016, the UK's summer public holiday falls on the dates between the 25th and 31st of August and is always a Monday. Therefore, we have something like this:

GotoIfTime(*,Mon,25-31,Aug?dest1,1)

This will catch all UK summer public holidays, and as there are no other Mondays in August clashing with these dates, it's a set-and-forget for many years (just don't forget to change it after 2016!). The same goes for the majority of other public holidays except for Easter.

For these variable dates, we can resort back to the internal database to store the details and then use the GotoIf() application to check if the date is a holiday.