Saturday, March 3, 2007

Propagating Asterisk MWI Across Multiple Servers

My experience with any type of technology is to get one thing working at a time. If you're relatively new to Asterisk I would suggest taking that approach with centralizing voicemail services. Get centralized voicemail working, then tackle MWI. If you're inexperienced with these concepts it could create a troubleshooting nightmare.

If you followed my last posting you've got centralized voicemail working. People can transfer messages across the organization regardless of what server their phone is registered to. Sooner or later (probably right away) you're going to want the Message Waiting Indicator (MWI) to start working.

Since Asterisk doesn't natively provide the ability to centralize voicemail it makes sense that it doesn't offer the ability to deploy MWI across multiple servers. Now, you could get in the source code and start hacking around, but that is overkill. With a better-than-newbie understanding of how voicemail and MWI services in Asterisk work, you can put together a solution to set the MWI on any phone in your organization, regardless of what server it is registered to.

There are a couple of things to understand about Asterisk voicemail and MWI:
  • MWI is turned on or off by the presence of a msgxxx.txt file in the INBOX directory for a given voicemail box (see http://asterisk.mdaniel.net/?p=14, and http://www.voip-info.org/wiki/view/Asterisk+MWI)
  • The "externnotify=" option in voicemail.conf allows you to run a program or script whenever a voicemail is received and also when someone exits the VoiceMailMain() application
  • When "externnotify" is processed it passes the context, extension and number of messages to the program or script you specify
In order to propagate MWI across all of my servers I pretty much follow the same strategy as other people who have done this before. However, I use a different tactic for the server to server communication. Instead of using ssh to pass the mailbox variables, I use a simple call over IAX. I really like this solution because I only have to maintain one type of connection and I don't have to setup ssh keys. If you happen to trunk your servers with SIP you could use that as well. The method I use is pretty much protocol agnostic.

I figured that I need to pass those three parameters to each of my non-voicemail servers: context, extension, number of messages. I googled around to see if I could set variables in IAX and pass them in a call. I found a lot of people requesting that ability, but didn't see the ability to actually do it. I thought about switching to SIP and setting custom headers, but that meant I would have to have an IAX trunk and a SIP trunk between each of my servers. I considered cramming all of the variables into the extension and parsing them out on the other side. Then it occurred to me that there is a way I could send three separate pieces of information without having to parse them on the other end. Here is what I ended up with:

Extension = Extension (aka voicemail box)

CallerIDName = Context
CallerIDNum = Number of Messages

It sounds like an ugly hack, but to me all that matters is that it works. All you need to transfer the status of a voicemail box between servers is a split second phone call.

Connecting the Servers


Since we are going to use a call from the voicemail server to the phone registration server to send the MWI status, you will need to create trunks that allow calls from your voicemail server to your phone registration servers. I prefer to associate these trunks with a seperate context called "setmwi". As I said before, creating an IAX trunk has been tutorialed to death, so I'm not going to waste the bits. Here is a diagram of the way I would do it.



Voicemail Server Configuration

Lets start with the externnotify script. When this script kicks off I want it to do the following

  • gather the extension, context, and number of messages variables
  • set those variables into the extension, calleridname and calleridnum of an IAX call to my phone registration servers
  • kick off the calls to my phone registration servers
My externnotify script creates .call files that accomplish the above objectives. For more info on .call files see the "Asterisk autodial out" page at the voip-info wiki. The script, which I've named notify_mwi.sh, is as follows:

#!/bin/bash

CONTEXT=$1
EXTEN=$2
NUMVMS=$3

cat < /var/spool/asterisk/1$EXTEN
Channel:IAX2/toDenver/$EXTEN
CallerID: $CONTEXT<$NUMVMS>
MaxRetries: 0
Extension: send
Context: notifymwi
Priority: 1
EOF
mv /var/spool/asterisk/1$EXTEN /var/spool/asterisk/outgoing

cat < /var/spool/asterisk/2$EXTEN
Channel:IAX2/toCSprings/$EXTEN
CallerID: $CONTEXT<$NUMVMS>
MaxRetries: 0
Extension: send
Context: notifymwi
Priority: 1
EOF
mv /var/spool/asterisk/2$EXTEN /var/spool/asterisk/outgoing


Create as many .call files as you have servers to notify. You'll notice that if the call connects successfully it passes it off to the local extension "send" in the context "notifymwi" on the voicemail server. This is really just a dummy extension on the voicemail server so that the call can be successfully connected. In extensions.conf the dummy extension looks like this:

[notifymwi]
exten => send,1,Answer
exten => send,2,wait(3) ;the other end will hangup fast anyway
exten => send,3,Hangup


Phone Registration Servers Configurations

I use the exact script to create and remove msgxxx.txt files that is posted at http://asterisk.mdaniel.net/?p=14. I don't want to take any credit I don't deserve!

Based upon the variables sent, the script creates the appropriate folders and msgxxx.txt files. If there is no voicemail, the script deletes any existing msgxxx.txt files. It looks like this:

#!/bin/bash

CONTEXT=$1
EXTEN=$2
NUMVMS=$3

NOCMD="rm -f /var/spool/asterisk/voicemail/$CONTEXT/$EXTEN/INBOX/*"
CREATECMD="mkdir -p /var/spool/asterisk/voicemail/$CONTEXT/$EXTEN/INBOX"

if [[ $NUMVMS = "0" ]]; then
$NOCMD
else
$NOCMD
$CREATECMD
for ((count = 1; count <= $NUMVMS; count++)); do
TOUCHCMD="touch /var/spool/asterisk/voicemail/$CONTEXT/$EXTEN/INBOX/msg$count.txt"
$TOUCHCMD
done
fi


Now we have to run the script when the voicemail server calls the phone registration servers. If configured properly, the IAX trunk is associated with the "setmwi" context. Utilizing the System() command, I can call the set_mwi.sh script when a call comes in to the "setmwi" context. Here is the entry in extensions.conf. All of the voicemail boxes in my scenario are four digit extensions so you may have to tweak this a bit depending on your specific application.

[setmwi]
exten => _XXXX,1,Answer
exten => _XXXX,2,System(/usr/local/bin/set_mwi.sh ${CALLERIDNAME} ${EXTEN} ${CALLERIDNUM})
exten => _XXXX,3,Hangup

Conclusion

That should be it. Leave a few voicemails and watch the "magic" happen. If you wanted to get fancy you could figure out a way to have the voicemail server only call the proper phone registration servers. To me it wasn't worth the complexity since the msgxxx.txt files don't take up much space and a half a second call doesn't take much bandwidth. Also, we have the same extension on multiple servers sometimes so it wouldn't be worth the administrative work to keep track of everything.

If you have any suggestions or refinements I would love to hear the feedback. Thanks!

*I should note that I have heard of some people using NFS to mount the voicemail directory from the voicemail server onto the voicemail directory of the phone registration servers. This allows the phone registration servers to see when a voicemail is left and the MWI gets set as it would normally. Depending on your environment, this may be a viable solution as well. Gotta love flexibility.

5 comments:

  1. Thanks for the Howto.

    I think there is <<EOF missing from the end of the cat statements in notify_mwi.sh

    ReplyDelete
  2. Nifty approach. Post this somewhere on voip-info.org (if you haven't already).

    -jr
    http://blog.joshrichards.org/

    ReplyDelete
  3. Thanks for the info, but there is a bit of very important information missing in your writeup. Where you say:

    "If configured properly, the IAX trunk is associated with the "setmwi" context."

    Uhm, what is "configured properly" then? When the script kicks off, it gets sent to my "from-internal" context and dials the extension. Please explain how to have this call go to a different context.

    Thanks.

    ReplyDelete
  4. asterisk 1.4 seems to need the message file to have four digits like this :-
    msg0001.txt

    ReplyDelete
  5. Great stuff! Thanks!
    A couple of notes:

    As previously mentioned, the double-caret marks in the notify_mwi.sh script are missing (stripped, no doubt, by blogger).

    In order for this to work on our system, we had to change the ownership of the call file before we moved it to the outgoing directory.

    Include the PID of the bash script in the call filename to eliminate any chance of a corrupted call file from simultaneous access.

    Asterisk 1.4 requires leading zeros, as mentioned. Just add leading zeros to the touch statement (msg000$count) (which is inelegant for $count > 9, but this is mwi so we don't really care about the count)

    Thanks again!

    ReplyDelete