当前位置:   article > 正文

利用mod_lua实现动态配置FreeSwtich

mod_lua

        FreeSwtich默认的配置体系是XML文件,修改配置后需要reloadxml生效。这对于大型线上系统,是不可接受的。FreeSwitch本身提供了几种不同的动态配置接口,比如mod_xml_curl,本文介绍利用mod_lua动态配置FS的方法。

        通过mod_lua模块,你可以利用lua实现动态配置。绑定一个脚本处理XML请求,就像mod_xml_curl那样,动态地把所需要的XML片段喂给FS,指示FS的执行。实现应用与数据的分离。FreeSWITCH需要某种XML信息时,它查询注册的钩子,调用指定的脚本。在脚本中,你可以为所欲为,只是不要忘记返回XML就行了。

配置mod_lua

        FreeSwitch启动时会加载conf/autoload_configs/lua.conf.xml配置。

        以下是一个极简配置实例:

  1. <configuration name="lua.conf" description="LUA Configuration">
  2.   <settings>
  3.     <param name="xml-handler-script" value="dp.lua"/>
  4.     <param name="xml-handler-bindings" value="dialplan"/>
  5.   </settings>
  6. </configuration>

  编辑lua.conf.xml之后,FreeSWITCH控制台命令reloadxml不能识别 "xml-handler-script" 参数,你必须重启FS才会生效 FreeSWITCH.


可配置标签

        对于"xml-handler-bindings"参数,可配置值有:"dialplan" "directory" "configuration" "dialplan|directory|configuration"

  • FS会向脚本传递一个XML_REQUEST对象,它包含以下成员:section、tag_name、key_name和key_value。它可用于查询模块配置、目录信息、拨号方案,还可能传递一个'params'属性,携带一个 event对象
  • 脚本执行完毕之后,你所设置的XML_STRING对象将返回给FS,无论你在里面设置了什么内容

        在Lua脚本中,可以访问XML_REQUEST对象获取必要的信息。比如:

freeswitch.consoleLog("notice", "SECTION " .. XML_REQUEST["section"] .. "\n")

模块配置

        如果mod_lua加载时有如下配置:

  1. <configuration name="lua.conf" description="LUA Configuration">
  2.   <settings>
  3.     <param name="xml-handler-script" value="configuration.lua"/>
  4.     <param name="xml-handler-bindings" value="configuration"/>
  5.   </settings>
  6. </configuration>

那么之后加载的模块,可以把mod_lua当成配置服务器,需要配置信息时,调用绑定的lua实时查询。

        查询模块配置的lua脚本中,隐含一个XML_REQUEST对象,其典型的属性值:

  • key_value = 'iax.conf'|'event_socket.conf'|sofia.conf'|...
  • key_name = 'name'
  • section = 'configuration'
  • tag_name = 'configuration'

 

'params' event object
acl.confnoN/A
event_socket.confnoN/A
post_load_switch.confnoN/A
sofia.confyes
  1. Event-Name: REQUEST_PARAMS
  2. Core-UUID: 0f8afb73-2183-a1e2-2316-71053c746130
  3. FreeSWITCH-Hostname: hostname
  4. FreeSWITCH-IPv4: 192.168.1.12
  5. FreeSWITCH-IPv6: %3A%3A1
  6. Event-Date-Local: 2010-08-06%2014%3A04%3A38
  7. Event-Date-GMT: Fri,%2006%20Aug%202010%2018%3A04%3A38%20GMT
  8. Event-Date-Timestamp: 1281117878629975
  9. Event-Calling-File: sofia.c
  10. Event-Calling-Function: config_sofia
  11. Event-Calling-Line-Number: 2637
switch.confnoN/A
syslog.confnoN/A

目录(directory)

启动初始化时

 

        在启动初始化过程中,会读取profile配置,为网关和别名域读取目录信息。

        在模块加载时,Lua脚本中的XML_REQUEST对象的属性值:

  • key_value = ''
  • key_name = ''
  • section = 'directory'
  • tag_name = ''

此外,"params" 携带一个event 对象。

        对sofia中的每个profile,directory都会读取一次。The directory is read once for each profile in the sofia configuration


    注意上面的变量值有“空字符串”,不是nil


Params event事件

对于External profile:

  1. Event-Name: REQUEST_PARAMS
  2. Core-UUID: <uuid>
  3. FreeSWITCH-Hostname: hostname
  4. FreeSWITCH-IPv4: 192.168.1.11
  5. FreeSWITCH-IPv6: %3A%3A1
  6. Event-Date-Local: 2010-08-06%2014%3A04%3A40
  7. Event-Date-GMT: Fri,%2006%20Aug%202010%2018%3A04%3A40%20GMT
  8. Event-Date-Timestamp: 1281117880813532
  9. Event-Calling-File: sofia.c
  10. Event-Calling-Function: config_sofia
  11. Event-Calling-Line-Number: 3481
  12. purpose: gateways
  13. profile: external

重要字段:

purpose: gateways

profile: external

...

FreeSWITCH-Hostname: hostname

FreeSWITCH-IPv4: 192.168.1.11

对于 Internal profile 

  1. Event-Name: REQUEST_PARAMS
  2. Core-UUID: <uuid>
  3. FreeSWITCH-Hostname: hostname
  4. FreeSWITCH-IPv4: 192.168.1.11
  5. FreeSWITCH-IPv6: %3A%3A1
  6. Event-Date-Local: 2010-08-06%2014%3A04%3A41
  7. Event-Date-GMT: Fri,%2006%20Aug%202010%2018%3A04%3A41%20GMT
  8. Event-Date-Timestamp: 1281117881174514
  9. Event-Calling-File: sofia.c
  10. Event-Calling-Function: config_sofia
  11. Event-Calling-Line-Number: 3481
  12. purpose: gateways
  13. profile: internal

 

重要字段:

purpose: gateways

profile: internal

...

FreeSWITCH-Hostname: hostname

FreeSWITCH-IPv4: 192.168.1.11

 

网络列表

        启动初始化过程中,还会从directory中读取网络列表,但这时的XML_REQUEST属性值有些差异:

  • key_value = <name-of-domain> (比如 192.168.1.11)
  • key_name = 'name'
  • section = 'directory'
  • tag_name = 'domain'

Event 报文

  1. Event-Name: REQUEST_PARAMS
  2. Core-UUID: <uuid>
  3. FreeSWITCH-Hostname: hostname
  4. FreeSWITCH-IPv4: 192.168.1.11
  5. FreeSWITCH-IPv6: %3A%3A1
  6. Event-Date-Local: 2010-08-06%2014%3A04%3A43
  7. Event-Date-GMT: Fri,%2006%20Aug%202010%2018%3A04%3A43%20GMT
  8. Event-Date-Timestamp: 1281117883173795
  9. Event-Calling-File: sofia_reg.c
  10. Event-Calling-Function: sofia_reg_parse_auth
  11. Event-Calling-Line-Number: 1797
  12. action: sip_auth
  13. sip_profile: internal
  14. sip_user_agent: IP-Phone-V3.2.49T5.13%20-%20G729
  15. sip_auth_username: 1000
  16. sip_auth_realm: 192.168.1.11
  17. sip_auth_nonce: <auth_nonce_uuid>
  18. sip_auth_uri: sip%3A192.168.1.11
  19. sip_contact_user: 1000
  20. sip_contact_host: 192.168.88.202
  21. sip_to_user: 1000
  22. sip_to_host: 192.168.1.11
  23. sip_to_port: 5060
  24. sip_from_user: 1000
  25. sip_from_host: 192.168.1.11
  26. sip_from_port: 5060
  27. sip_request_host: 192.168.1.11
  28. sip_auth_qop: auth
  29. sip_auth_cnonce: 829326
  30. sip_auth_nc: 00000001
  31. sip_auth_response: <auth_response - md5sum?>
  32. sip_auth_method: REGISTER
  33. key: id
  34. user: 1000
  35. domain: 192.168.1.11
  36. ip: 192.168.88.202

重要字段:

domain: 192.168.1.11

purpose: network-list

...

FreeSWITCH-Hostname: hostname

FreeSWITCH-IPv4: 192.168.1.11

 

查找用户

        FreeSWITCH以特定域标签查找用户,这时XML_REQUEST对象的属性值:

  • key_value = '<name-of-domain>'
  • key_name = 'name'
  • section = 'directory'
  • tag_name = 'domain'

and 'params' will have an event object

注册时(REGISTER)

Event 报文:

  1. Event-Name: REQUEST_PARAMS
  2. Core-UUID: <uuid>
  3. FreeSWITCH-Hostname: hostname
  4. FreeSWITCH-IPv4: 192.168.1.11
  5. FreeSWITCH-IPv6: %3A%3A1
  6. Event-Date-Local: 2010-08-06%2014%3A04%3A43
  7. Event-Date-GMT: Fri,%2006%20Aug%202010%2018%3A04%3A43%20GMT
  8. Event-Date-Timestamp: 1281117883173795
  9. Event-Calling-File: sofia_reg.c
  10. Event-Calling-Function: sofia_reg_parse_auth
  11. Event-Calling-Line-Number: 1797
  12. action: sip_auth
  13. sip_profile: internal
  14. sip_user_agent: IP-Phone-V3.2.49T5.13%20-%20G729
  15. sip_auth_username: 1000
  16. sip_auth_realm: 192.168.1.11
  17. sip_auth_nonce: <auth_nonce_uuid>
  18. sip_auth_uri: sip%3A192.168.1.11
  19. sip_contact_user: 1000
  20. sip_contact_host: 192.168.88.202
  21. sip_to_user: 1000
  22. sip_to_host: 192.168.1.11
  23. sip_to_port: 5060
  24. sip_from_user: 1000
  25. sip_from_host: 192.168.1.11
  26. sip_from_port: 5060
  27. sip_request_host: 192.168.1.11
  28. sip_auth_qop: auth
  29. sip_auth_cnonce: 829326
  30. sip_auth_nc: 00000001
  31. sip_auth_response: <auth_response - md5sum?>
  32. sip_auth_method: REGISTER
  33. key: id
  34. user: 1000
  35. domain: 192.168.1.11
  36. ip: 192.168.88.202

重要字段:

key: id

user: 1000

domain: 192.168.1.11

...

action: sip_auth

sip_profile: internal

FreeSWITCH-Hostname: hostname

FreeSWITCH-IPv4: 192.168.1.11

ip: 192.168.88.202

呼出 (INVITE)

Event 报文

  1. Event-Name: REQUEST_PARAMS
  2. Core-UUID: <uuid>
  3. FreeSWITCH-Hostname: hostname
  4. FreeSWITCH-IPv4: 192.168.1.11
  5. FreeSWITCH-IPv6: %3A%3A1
  6. Event-Date-Local: 2010-08-06%2016%3A28%3A08
  7. Event-Date-GMT: Fri,%2006%20Aug%202010%2020%3A28%3A08%20GMT
  8. Event-Date-Timestamp: 1281126488274011
  9. Event-Calling-File: sofia_reg.c
  10. Event-Calling-Function: sofia_reg_parse_auth
  11. Event-Calling-Line-Number: 1797
  12. action: sip_auth
  13. sip_profile: internal
  14. sip_user_agent: IP-Phone-V3.2.49T5.13%20-%20G729
  15. sip_auth_username: 1001
  16. sip_auth_realm: 192.168.1.11
  17. sip_auth_nonce: 1fe3d1fa-a199-11df-b392-b105e374638e
  18. sip_auth_uri: sip%3A1000%40192.168.1.11
  19. sip_contact_user: 1001
  20. sip_contact_host: 192.168.88.99
  21. sip_to_user: 1000
  22. sip_to_host: 192.168.1.11
  23. sip_to_port: 5060
  24. sip_from_user: 1001
  25. sip_from_host: 192.168.1.11
  26. sip_from_port: 5060
  27. sip_request_user: 1000
  28. sip_request_host: 192.168.1.11
  29. sip_auth_qop: auth
  30. sip_auth_cnonce: 10560d0
  31. sip_auth_nc: 00000001
  32. sip_auth_response: b99d1213022480a2b6c4e14432661821
  33. sip_auth_method: INVITE
  34. key: id
  35. user: 1001
  36. domain: 192.168.1.11
  37. ip: 192.168.88.99

重要字段:

key: id

user: 1001

domain: 192.168.1.11

...

ip: 192.168.88.99

FreeSWITCH-Hostname: hostname

FreeSWITCH-IPv4: 192.168.1.11

action: sip_auth

sip_profile: internal

 

呼入

Event 报文

  1. Event-Name: REQUEST_PARAMS
  2. Core-UUID: <uuid>
  3. FreeSWITCH-Hostname: hostname
  4. FreeSWITCH-IPv4: 192.168.1.11
  5. FreeSWITCH-IPv6: %3A%3A1
  6. Event-Date-Local: 2010-08-06%2016%3A28%3A09
  7. Event-Date-GMT: Fri,%2006%20Aug%202010%2020%3A28%3A09%20GMT
  8. Event-Date-Timestamp: 1281126489462522
  9. Event-Calling-File: mod_dptools.c
  10. Event-Calling-Function: user_outgoing_channel
  11. Event-Calling-Line-Number: 2662
  12. as_channel: true
  13. action: user_call
  14. key: id
  15. user: 1000
  16. domain: 192.168.1.11

重要字段:

key: id

user: 1000

domain: 192.168.1.11

...

FreeSWITCH-Hostname: hostname

FreeSWITCH-IPv4: 192.168.1.11

as_channel: true

Event-Calling-Function: user_outgoing_channel

 

拨号方案

配置

  1. <configuration name="lua.conf" description="LUA Configuration">
  2.   <settings>
  3.     <param name="xml-handler-script" value="dp.lua"/>
  4.     <param name="xml-handler-bindings" value="dialplan"/>
  5.   </settings>
  6. </configuration>

Lua 脚本 dp.lua示例

  1. -- params is the event passed into us we can use params:getHeader to grab things we want.
  2. io.write("TEST\n" .. params:serialize("xml") .. "\n");
  3. mydialplan = [[
  4. <?xml version="1.0" encoding="UTF-8" standalone="no"?>
  5. <document type="freeswitch/xml">
  6. <section name="dialplan" description="LUA Dial Plan For FreeSwitch">
  7. <context name="default">
  8. <extension name="lua_dp_ext">
  9. <condition field="destination_number" expression="^10086$">
  10. <action application="sleep" data="1000 "/>
  11. <action application="answer"/>
  12. <action application="playback" data="ivr/8000/ivr-welcome_to_freeswitch.wav"/>
  13. </condition>
  14. </extension>
  15. </context>
  16. </section>
  17. </document>
  18. ]]
  19. XML_STRING = mydialplan
  20. -- comment the following line for production:
  21. freeswitch.consoleLog("notice", "Debug XML:\n" .. XML_STRING .. "\n")

同一功能的另一个版本:

  1. -- LUA dialplan
  2. DIALPLAN = {}
  3. freeswitch.consoleLog("notice", "SECTION " .. XML_REQUEST["section"] .. "\n")
  4. table.insert(DIALPLAN, [[<?xml version="1.0" encoding="UTF-8" standalone="no"?>]])
  5. table.insert(DIALPLAN, [[<document type="freeswitch/xml">]])
  6. table.insert(DIALPLAN, [[ <section name="dialplan" description="LUA Dial Plan For FreeSwitch">]])
  7. table.insert(DIALPLAN, [[ <context name="default">]])
  8. table.insert(DIALPLAN, [[ <extension name="lua_dp_ext">o]])
  9. table.insert(DIALPLAN, [[ <condition field="destination_number" expression="^10086$">]])
  10. table.insert(DIALPLAN, [[ <action application="sleep" data="1000 "/>]])
  11. table.insert(DIALPLAN, [[ <action application="answer"/>]])
  12. table.insert(DIALPLAN, [[ <action application="playback" data="ivr/8000/ivr-welcome_to_freeswitch.wav"/>]])
  13. table.insert(DIALPLAN, [[ </condition>]])
  14. table.insert(DIALPLAN, [[ </extension>]])
  15. table.insert(DIALPLAN, [[ </context>]])
  16. table.insert(DIALPLAN, [[ </section>]])
  17. table.insert(DIALPLAN, [[</document>]])
  18. XML_STRING = table.concat(DIALPLAN, "\n")
  19. mydialplan = [[
  20. <document type="freeswitch/xml">
  21. <section name="dialplan" description="LUA Dial Plan For FreeSwitch">
  22. <context name="default">
  23. <extension name="lua_dp_ext">
  24. <condition field="destination_number" expression="^10086$">
  25. <action application="sleep" data="1000 "/>
  26. <action application="answer"/>
  27. <action application="playback" data="ivr/8000/ivr-welcome_to_freeswitch.wav"/>
  28. </condition>
  29. </extension>
  30. </context>
  31. </section>
  32. </document>
  33. ]]
  34. -- comment the following line for production:
  35. freeswitch.consoleLog("notice", "Debug XML:\n" .. XML_STRING .. "\n")

 

通过profile设置跳转

        上述实例,展示了lua是怎样生成XML片段并喂给FS执行的。但是拼接XML的过程显得有些繁琐。有没有办法简单一点呢?Dialplan的描述本质上无非就是一组的Action加上参数而已。

         lua_dialplan_hunt的实现中,允许使用ACTIONS表来传递这些信息,换句话说,在脚本里,构建一个ACTIONS table并传回给FS就可以了。那么我们需要怎样做呢?

profile设置

        编辑sip profile配置文件,比如说freeswitch/sip_profiles/internal.xml,修改以下两个属性:

  1. <param name="context" value="luadp.lua"/>
  2. <param name="dialplan" value="LUA"/>

用户目录配置

        修改用户的context属性为“luadp.lua”,比如说freeswitch/directory/default/1000.xml:

     <variable name="user_context" value="luadp.lua"/>

添加lua脚本

        在脚本目录下,添加一个名为luadp.lua的脚本,内容如下:

  1. -- LUA dialplan
  2. ACTIONS = {}
  3. freeswitch.consoleLog("notice", "from your script")
  4. table.insert(ACTIONS, {"sleep", "1000"})
  5. table.insert(ACTIONS, "answer")
  6. table.insert(ACTIONS, {"playback", "ivr/8000/ivr-welcome_to_freeswitch.wav"})
  7. table.insert(ACTIONS, {"log", "NOTICE after your script"})

        这个实例是前面dialplan的第三个版本,脚本逻辑是不是简单得多了。


Lua调用XML dialplan:

 

table.insert(ACTIONS, {"transfer", "123 XML some-context"})

 XML 调用Lua dialplan:

<action application="transfer" data="123 LUA some-dialplan.lua"/>

Not found

        如果LUA 收到一个dialplan查询请求,但没有相应处理逻辑,那么应当返回 "not found" 结果(如下所示),不能返回空串:

  1. <?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2. <document type="freeswitch/xml">
  3.   <section name="result">
  4.     <result status="not found" />
  5.   </section>
  6. </document>

如果返回一个空串,FS将处理报错:

[ERR] switch_xml.c:1534 switch_xml_locate() Error[[error near line 1]: root tag missing]

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/148650?site
推荐阅读
相关标签
  

闽ICP备14008679号