Load balance SIP extensions on two asterisk servers

I would like to load balance all my local SIP extensions with 2 Asterisk/FreePBX servers PBX1/PBX2 on my LAN.

Both servers are identical: same freepbx configuraion, same dialplan (in case one server fails, the other will take care of all extensions and nobody should notice the server failure).

Supposing my exensions are 4XXX, they will be registering randomly to both PBX1 and PBX2 (either with rrDNS or LinuxHA).
Statistically, 50% of my extensions will be on PBX1 and 50% on PBX2.

How do I configure freepbx so that, say, SIP extension 4001 registered on PBX1 can call SIP/4002 on PBX2?

I figure I could create a IAX2 trunk between the 2 servers (type=friend).
Then create an outgoing route with dial rule “4XXX” which uses the IAX2 trunk.
Finally, for extension 4002 define a follow-me list including 4002 and 4002#.

But since PBX2 would have the same freepbx configuration/dialplan then wouldn’t this lead to an infinite loop if, say, extension 4002 is unavailable/not registered on PBX2?

Or maybe I could avoid defining an outgoing route and use the “dial” field for each SIP extension. Instead of leaving it as, say, “SIP/4002” I might use something like “SIP/4002&IAX2/pbx2trunk:[email protected]/4002”.
But then again, if both servers have the same dialplan logic and serve the same extension ranges then I might end up with an infinite loop between the 2 PBXs.

I’m sure other users have already dealt with this situation.
What would be the simplest approach?
Does FreePBX already do this?

Thanks,

Vieri

Hi,

Can you give some more information on why you are trying to do this? Is it for high availability? Do you have a large number of extensions?

With some of the background we will hopefully be able to help you with a solution.

Thanks

Graham

Yes. It’s for high availability.
What I would like to achieve is the following, simply put:

PBX1 and PBX2 are identical servers (or practically identical). Most important, their dialplans are the same and they “serve” the same range of extensions, say, 4XXX. Both have FreePBX 2.4.0 with updated modules to yesterday.

LAN SIP clients will be connecting randomly to either one of these servers. Consequently, they need to contact peers that can be registered on the same or on the other server.

I think that the most elegant way to do this is with DUNDI and sip’s “regcontext”.
I’m reading this guide:
http://www.astricon.net/files/usa06/Friday-General_Conference/JR_Richardson_Whitepaper.pdf
which is very informative but I hope I can find a way to do the same thing without Realtime and with FreePBX. In fact, I don’t see why it shouldn’t work with plain .conf files (but then again I may be missing something).
My situation is a lot simpler than the one described in the guide.
I just have 2 servers and they do everything: register, dundi, dialplan.

I’m having quite a hard time getting a grasp on DUNDi with FreePBX. “dundi lookups” on the *CLI work fine, with or without “regcontext”. However, I don’t see how the instructions on the “Add DUNDi Trunk” on the FreePBX page can work unless one defines an outgoing route which specifically uses this trunk. Otherwise “switch => dundi/priv” will never be called.

I got something “working” somehow by doing the following:

  1. created everything as stated in http://freepbx.org/trac/ticket/2644.
  2. added “include => ext-local” in dundi-extens
  3. added an outgoing route which uses DUNDI/priv trunk and has dial rule 77773|4XXX
  4. for each SIP extension add a follow-me list which includes local extension and outgoing extension. For example, for extension 4001, I would define follow-me with the list:
    4001
    777734001#

I don’t really like this method because it doesn’t really need DUNDi. It could very well use just a plain IAX2 friend trunk between the 2 servers.
Also, I haven’t tested this config on both servers so I still don’t know if it will go into an infinite loop if a given SIP extension isn’t registered on any server.

I would like to use DUNDi with SIP’s regcontext feature for the latter reason.

Can anyone please give me a clue as to what I could try.

I hope I’ve given enough details. If not then please let me know.

Thanks

I managed to make DUNDi partially work for my situation. But I would like to have some feedback because the FreePBX source code needs hacking.

Here’s what I did.

in extensions_custom.conf:

[macro-local-or-dundi]
exten => s,1,Set(CHANTOCALL=${ARG1})
exten => s,2,Set(EXTTOCALL=${CHANTOCALL:4})
exten => s,3,Set(DUNDIMAP=${ARG2})
exten => s,4,ChanIsAvail(${CHANTOCALL}|sj)
exten => s,105,NoOp(${CHANTOCALL} is not available. Calling ${EXTTOCALL} via dundi-${DUNDIMAP}.)
exten => s,106,Macro(user-callerid,SKIPTTL,)
exten => s,107,Macro(record-enable,${EXTTOCALL},IN)
exten => s,108,AGI(fixlocalprefix)
exten => s,109,Macro(dundi-${DUNDIMAP},${EXTTOCALL}) ; use DUNDi if channel is unavailable
exten => s,110,Macro(hangupcall)

[dundi-incoming]
include => ext-local

cat iax_additional.conf

[priv]
type=friend ; or peer
dbsecret=dundi/secret
context=dundi-incoming

in dundi.conf I set the mapping to:

priv => dundi-extens,0,IAX2,priv:${SECRET}@192.168.250.128/${NUMBER},nopartial

cat sip_registrations_custom.conf

regcontext=dundi-extens

in extensions_additional.conf
in ext-local:

exten => 4065,1,Macro(local-or-dundi,SIP/4065,4)
exten => 4065,n,Macro(exten-vm,novm,4065)
exten => 4065,n,Hangup
exten => 4065,hint,SIP/4065

So this setup works fine if I call an extension directly.

However, if I try to call a ring group then things still don’t work because ext-group is different and I would need to modify macro-dial.
The latter macro may have to be re-written to allow dundi lookup.

Can anyone suggest a simpler way of doing this?

Sorry to post the code below but I can’t create a new ticket on the FreePBX Trac web site like I used to. Now it seems that “TICKET_CREATE_SIMPLE privileges are required to perform this operation”.

I would like to have some feedback from the developers and users regarding DUNDi setups similar to what I described above.

I seem to have the basics working (even ring groups) but I’m having some minor issues with CDRs.

The thing I really would like to solve is:

CDR (as taken from the reports page) on pbx2 where ext. 4053 is calling to ring group 4051:

  1.  2008-02-28 16:51:29  	SIP/4053-b...  	7021  	"VIERI" <7021>  	4051  	Dial...  	IAX2/priv:...  	ANSWERED  	01:11  	01:09
    

CDR on pbx1 where ext. 4065 which is a member of 4051 is being reached:

  1.  2008-02-28 16:52:52  	IAX2/priv-...  	7021  	"VIERI" <7021>  	4065  	ResetCDR...  	w...  	ANSWERED  	00:09  	00:04
    
  2.  2008-02-28 16:52:44 	IAX2/priv-... 	7021 	"VIERI" <7021> 	4065 	Busy... 	20... 	BUSY 	00:00 	00:00
    
  3.  2008-02-28 16:52:35 	IAX2/priv-... 	7021 	"VIERI" <7021> 	4065 	Busy... 	20... 	BUSY 	00:00 	00:00
    
  4.  2008-02-28 16:52:26 	IAX2/priv-... 	7021 	"VIERI" <7021> 	4065 	Busy... 	20... 	BUSY 	00:00 	00:00
    
  5.  2008-02-28 16:52:18 	IAX2/priv-... 	7021 	"VIERI" <7021> 	4065 	Busy... 	20... 	BUSY 	00:00 	00:00
    
  6.  2008-02-28 16:52:09 	IAX2/priv-... 	7021 	"VIERI" <7021> 	4065 	Busy... 	20... 	BUSY 	00:00 	00:00
    
  7.  2008-02-28 16:52:00 	IAX2/priv-... 	7021 	"VIERI" <7021> 	4065 	Busy... 	20... 	BUSY 	00:00 	00:00
    
  8.  2008-02-28 16:51:51 	IAX2/priv-... 	7021 	"VIERI" <7021> 	4065 	Busy... 	20... 	BUSY 	00:00 	00:00
    

Never mind the slight clock skew between pbx1 and pbx2.

The CDR “problem” I see is that pbx1 is reporting that my extension 4053 (aliased 7021) is trying to reach ext. 4065 but I actually dialed 4051 which is the ring group as seen in the CDR of PBX2.

How can I “tell” (and “when”) pbx1 that its CDR must relate somehow to the group 4051. I know that its logical that it logs the final ext. 4065 because that’s what’s performed but the GoTo in the macro-dundi generated by FreePBX. However, I would like to know if I can “tag” it somehow with the original dst (or relate it easily by doing a cross SQL query between the 2 servers).

Finally, could the developers please let me know if they are willing to consider the following “code logic” for future releases (ie. modify the “dial logic” so as to include DUNDi lookups)?

--- extensions.conf_ORIG 2008-02-28 18:39:20.000000000 +0100 +++ extensions.conf 2008-02-28 18:39:17.000000000 +0100 @@ -70,7 +70,11 @@ exten => s,n(dial),AGI(dialparties.agi) exten => s,n,NoOp(Returned from dialparties with no extensions to call and DIALSTATUS: ${DIALSTATUS})

-exten => s,n+2(normdial),Dial(${ds}) ; dialparties will set the priority to 10 if $ds is not null
+exten => s,n+2(normdial),Set(EXTTODIAL=${CUT(ds|&|1)})
+exten => s,n,NoOp(DUNDi lookup ${EXTTODIAL} extracted from ${ds})
+exten => s,n,Macro(local-or-dundi,${EXTTODIAL},4)
+exten => s,n,NoOp(DUNDi did not find ${EXTTODIAL}. Going local.)
+exten => s,n,Dial(${ds}) ; dialparties will set the priority to 10 if $ds is not null
exten => s,n,Set(DIALSTATUS=${IF($["${DIALSTATUS_CW}"!="" ]?${DIALSTATUS_CW}:${DIALSTATUS})})

exten => s,20(huntdial),NoOp(Returned from dialparties with hunt groups to dial )
@@ -97,7 +101,10 @@
exten => s,n,Set(CTLoop=$[1 + ${CTLoop}])
exten => s,n,Goto(s,a37)

-exten => s,n(a42),Dial(${${HuntMember}}${ds})
+exten => s,n(a42),NoOp(DUNDi lookup ${${HuntMember}})
+exten => s,n,Macro(local-or-dundi,${${HuntMember}},4)
+exten => s,n,NoOp(DUNDi did not find ${${HuntMember}}. Going local.)
+exten => s,n,Dial(${${HuntMember}}${ds})
exten => s,n,Set(HuntLoop=$[1 + ${HuntLoop}])
exten => s,n,GotoIf($[$[$[“foo${RingGroupMethod}” != “foofirstavailable”] & $[“foo${RingGroupMethod}” != “foofirstnotonphone”]] | $[“foo${DialStatus}” = “fooBUSY”]]?a46)
exten => s,n,Set(HuntMembers=0)

In sip_registrations_custom.conf:

regcontext=dundi-extens

In extensions_custom.conf:

[macro-local-or-dundi]
exten => s,1,Set(CHANTOCALL=${ARG1})
exten => s,2,Set(EXTTOCALL=${CUT(CHANTOCALL|/|2})
exten => s,3,Set(DUNDIMAP=${ARG2})
exten => s,4,ChanIsAvail(${CHANTOCALL}|sj)
exten => s,105,NoOp(DUNDi: ${CHANTOCALL} is not available locally. Calling ${EXTTOCALL} via dundi-${DUNDIMAP}.)
exten => s,106,Macro(user-callerid,SKIPTTL,)
exten => s,107,Macro(record-enable,${EXTTOCALL},IN)
exten => s,108,AGI(fixlocalprefix)
exten => s,109,Macro(dundi-${DUNDIMAP},${EXTTOCALL}) ; use DUNDi if channel is unavailable
exten => s,110,Macro(hangupcall)

[dundi-incoming]
include => from-internal

In FreePBX I created a DUNDi trunk with priv mapping and a IAX2 trunk.

iax_additional.conf:

[priv]
type=friend
dbsecret=dundi/secret
context=dundi-incoming

dundi.conf:

[general]
entityid=00:1D:60:39:E9:1B
cachetime=5
ttl=2
autokill=yes

[mappings]
priv => dundi-extens,0,IAX2,priv:${SECRET}@192.168.250.128/${NUMBER},nopartial

[00:1D:60:B0:25:10]
model = symmetric
host = 192.168.250.27
inkey = dundi
outkey = dundi
include = priv
permit = priv
qualify = yes
order = primary

Would really appreciate some feedback.

Regards,

Vieri

Hi,

From what I can see your configuration will not be a high availability solution. If you are trying to issue two different IPs using 2 A records in DNS, then if one of your switches stops working then 50% of your phones will go with it as well. They won’t just fail over to the other switch. Even if you reboot all your phones, 50% of them will try to connect to the dead switch.

Have you thought about using a secondary proxy instead? Some phones (like Aastra) allow you to specify a primary and secondary SIP proxy. All your phones would connect to the primary server (Which makes routing much simpler) and in the event of it failing, they will try to connect to the secondary instead.

All you need to do is make sure your configurations are the same, which is probably as simple as doing a FreePBX backup and then restoring it onto your secondary switch.

Your other option is to follow on of the HA how to guides for setting up a high availability asterisk server using disk replication and a single IP.

Comments?

Graham

Thanks for the feedback. I appreciate it.

As far as High Availability is concerned, I shouldn’t have mentioned rrDNS but only Heartbeat. So with my setup, SIP phones will always try to connect to a single IP address but Heartbeat will distribute the load to pbx1 and pbx2. It’s not just a failover setup as you mention for the secondary proxy solution. I am trying to do a master-master or active-active cluster of 2 identical servers. The big advantage of this setup is that under normal conditions I take the most power out of both servers. however, if I want to bring one of the two down for maintenance then all I need to do is “tell heartbeat” to accept re-registrations only to one of the two. After a while (depending on registration timeout) all SIP clients will be registered just to one server so I can work at ease. Once the server is back up, heartbeat will load balance sip registrations to both servers again. Of course, if one of the 2 servers should fail (software or hardware catastrophy) then around 50% of active channels would get disconnected but at least the users could re-dial almost immediately because of the short re-registration timeouts I’ve set.

My first (and inexperienced) approach to DUNDi seems to work except for some gotchas with CDRs and keeping some data of astdb in sync (both ways) between the two servers (eg. when I set a value in the DB on pbx1 I also have to set it on pbx2 and vice versa).

I’d be grateful if someone could point me to a complete HA guide which can be applied to */FreePBX and can be used in master-master mode (I have seen a few examples of disk replication and HA but that was for a master-slave setup).

Thanks!

As a side note, I think you can do HA with DNS SRV. Correct me if I’m wrong but DNS SRV would allow a failover setup just like e-mail exchangers. But like I said, I don’t want an active-passive but an active-active cluster. That’s why I found DUNDi interesting but I’d be happy to follow another path if someone is willing to share their thoughts.
Thanks for your time.

To get your trac posting rights back do the following. logout then log back in. the trac cookie expired or got corrupt. if you logout and back in it will then work.

Does such guide exist?

The interesting thing is when we design and architect a server or a dedicated server , we don’t design it for Windows or Linux, we design it for both. We don’t really care, as long as we’re selling the one the customer wants. The technologies we use now a days are a combination of Linux Xen, KVM and Microsoft Hyper-V. We utilize the latest Intel VT extensions to give optimum performance.

the way to go is to use heartbeat with drbd. i have dozens of such HA clusters running with excellent results. this setup creates an active/passive server cluster and the critical data [i.e. the freepbx database and the config files etc] are resident on the equivalent of a network RAID drive that is available to the active server at any given time. the cluster will have its own floating IP which is resident on the active server and moves to the other server in the event of a failover.

i am now working on bigger clusters using the same approach [3 active, 1 passive] to incorporate load balancing as well as HA redundancy.

here is a useful starting point: http://www.drbd.org/users-guide/

there are a couple of guides that have been written specific to various asterisk distros [trixbox and elastix] although at this point those guides are somewhat out of date.

@mudslide567 - did you made it with 3 active, 1 passive FreePBX servers. I’m having trouble with it?

If you get this operational(as you so seem to at the moment) I’d like to get a full copy of each of the pbx1 and pbx2 fully configured I can then set it up in our test bed and tweak it for you and then return them once our developing team completes what you so terrifically have gotten to work

send to [email protected]

great work

or you can send to an FTP server you have or we can give you a link for uploading