SlideShare a Scribd company logo
Sebastian Damm
E: damm@sipgate.de
T: @_SebastianDamm
Modular And Test Driven SIP
Routing With Lua
Who we are, what we do
• Düsseldorf based VoIP service provider (since 2004)
• Active in Germany and UK
• Full MVNO in the Telefónica network
• Private and Business customers
• VoIP and Mobile products
• Some 100k active customers
• Almost 100 million minutes each month
Of course, we use Kamailio
Of course, we use Kamailio
All over our network.
Of course, we use Kamailio
All over our network.
But we have a huge problem!
What problem?
at sipgate
• methods of software development have changed:
- Agile, fast cycles, many releases
- small modules
- unit tests, continuous deployment
- permanent refactoring of functions
What problem?
at sipgate
• methods of software development have changed:
- Agile, fast cycles, many releases
- small modules
- unit tests, continuous deployment
- permanent refactoring of functions
• telephony side:
- "organically grown" routing logic (since 2004)
- 40 developers, but only 4 people can read/write it
- learnings from software development haven't made it to this part
What problem?
Nobody wants to touch it!
What problem?
1. Readability
What problem?
1. Readability
What problem?
1. Readability
What problem?
2. Config file size
What problem?
2. Config file size
What problem?
2. Config file size
What problem?
3. Testability
What problem?
3. Testability
Untested!
What problem?
It's a mess!
But how do we fix that?
But how do we fix that?
• app_*:
- app_lua
- app_python
- app_jsdt
- app_java, app_mono, app_perl
- missing: app_ruby ;)
But how do we fix that?
• app_*:
- app_lua
- app_python
- app_jsdt
- app_java, app_mono, app_perl
- missing: app_ruby ;)
• KEMI
- lua
- python
- jsdt
- sqlang
Why Lua?
1. Speed
Interpreter Average Min Max
Native 302.28 6 3824
Lua 308.32 6 3596
Python 393.71 23 3266
All times in Microseconds.

Source: https://www.kamailio.org/wiki/devel/config-engines#interpreters_performances
Why Lua?
2. Easy to learn
• None of us had written Lua (production) code before.
• If you've written code before, you'll be able to start right away.
• Less fear of changing something within Kamailio.
Why Lua?
3. You can work test-driven
• Impossible with native Kamailio language
• Know if you broke something before you deploy it!
Standard way: one big Lua file
Still many lines of code
Standard way: one big Lua file
Still have to know Kamailio syntax all the time
function ksr_request_route()
-- per request initial checks
ksr_route_reqinit();
-- NAT detection
ksr_route_natdetect();
-- CANCEL processing
if KSR.pv.get("$rm") == "CANCEL" then
if KSR.tm.t_check_trans()>0 then
ksr_route_relay();
end
return 1;
end
-- handle requests within SIP dialogs
ksr_route_withindlg();
-- -- only initial requests (no To tag)
-- handle retransmissions
if KSR.tmx.t_precheck_trans()>0 then
KSR.tm.t_check_trans();
return 1;
end
if KSR.tm.t_check_trans()==0 then return 1 end
-- authentication
ksr_route_auth();
-- remove preloaded route headers
KSR.hdr.remove("Route");
if string.find("INVITE|SUBSCRIBE", KSR.pv.get("$rm")) then
KSR.rr.record_route();
end
-- account only INVITEs
if KSR.pv.get("$rm")=="INVITE" then
KSR.setflag(FLT_ACC); -- do accounting
end
-- dispatch requests to foreign domains
ksr_route_sipout();
-- -- requests for my local domains
-- handle registrations
ksr_route_registrar();
if KSR.pv.is_null("$rU") then
-- request with no Username in RURI
KSR.sl.sl_send_reply(484,"Address Incomplete");
return 1;
end
-- user location service
ksr_route_location();
return 1;
end
Standard way: one big Lua file
Still not testable
function ksr_request_route()
-- per request initial checks
ksr_route_reqinit();
-- NAT detection
ksr_route_natdetect();
-- CANCEL processing
if KSR.pv.get("$rm") == "CANCEL" then
if KSR.tm.t_check_trans()>0 then
ksr_route_relay();
end
return 1;
end
-- handle requests within SIP dialogs
ksr_route_withindlg();
-- -- only initial requests (no To tag)
-- handle retransmissions
if KSR.tmx.t_precheck_trans()>0 then
KSR.tm.t_check_trans();
return 1;
end
if KSR.tm.t_check_trans()==0 then return 1 end
-- authentication
ksr_route_auth();
-- remove preloaded route headers
KSR.hdr.remove("Route");
if string.find("INVITE|SUBSCRIBE", KSR.pv.get("$rm")) then
KSR.rr.record_route();
end
-- account only INVITEs
if KSR.pv.get("$rm")=="INVITE" then
KSR.setflag(FLT_ACC); -- do accounting
end
-- dispatch requests to foreign domains
ksr_route_sipout();
-- -- requests for my local domains
-- handle registrations
ksr_route_registrar();
if KSR.pv.is_null("$rU") then
-- request with no Username in RURI
KSR.sl.sl_send_reply(484,"Address Incomplete");
return 1;
end
-- user location service
ksr_route_location();
return 1;
end
How about a library?
• Encapsulate Kamailio functions in easy-to-use native functions
• Re-use often used calls
• Testable
• Fix logic errors before deploying them into production
if KSR.pv.get("$rm") == "INVITE" then
[..]
end
if kamailio.is_invite() then
[..]
end
How about a library?
Reduce the main script to a minimum
kamailio = require("kamailio")
function ksr_request_route()
kamailio.process_request()
end
So how do we get there?
• Install dependencies
- Interpreter: lua, version 5.1 (apt-get install lua-5.1)
- Testing framework: busted (apt-get install lua-busted)
• Create your library.
• Define your function.
• Write tests.
• Make the function work.
Create your library
• Can be just one file containing all your methods

/etc/kamailio/kamailio-functions.lua

/usr/local/share/lua/5.1/kamailio-functions.lua
Create your library
• Can be just one file containing all your methods

/etc/kamailio/kamailio-functions.lua

/usr/local/share/lua/5.1/kamailio-functions.lua
• Can be a directory containing multiple files

/usr/local/share/lua/5.1/kamailio/

!"" actions.lua

!"" init.lua

!"" message.lua

!"" message_state.lua

!"" nathandling.lua

!"" security.lua

#"" traffic.lua
Create your library
• Can be just one file containing all your methods

/etc/kamailio/kamailio-functions.lua

/usr/local/share/lua/5.1/kamailio-functions.lua
• Can be a directory containing multiple files

/usr/local/share/lua/5.1/kamailio/

!"" actions.lua

!"" init.lua

!"" message.lua

!"" message_state.lua

!"" nathandling.lua

!"" security.lua

#"" traffic.lua
Create your library
• Can be just one file containing all your methods

/etc/kamailio/kamailio-functions.lua

/usr/local/share/lua/5.1/kamailio-functions.lua
• Can be a directory containing multiple files

/usr/local/share/lua/5.1/kamailio/

!"" actions.lua

!"" init.lua

!"" message.lua

!"" message_state.lua

!"" nathandling.lua

!"" security.lua

#"" traffic.lua
damm@stretch:~$ lua
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio
> foo = require "foo"
stdin:1: module 'foo' not found:
no field package.preload['foo']
no file './foo.lua'
no file '/usr/local/share/lua/5.1/foo.lua'
no file '/usr/local/share/lua/5.1/foo/init.lua'
no file '/usr/local/lib/lua/5.1/foo.lua'
no file '/usr/local/lib/lua/5.1/foo/init.lua'
no file '/usr/share/lua/5.1/foo.lua'
no file '/usr/share/lua/5.1/foo/init.lua'
no file './foo.so'
no file '/usr/local/lib/lua/5.1/foo.so'
no file '/usr/lib/x86_64-linux-gnu/lua/5.1/foo.so'
no file '/usr/lib/lua/5.1/foo.so'
no file '/usr/local/lib/lua/5.1/loadall.so'
stack traceback:
[C]: in function 'require'
stdin:1: in main chunk
[C]: ?
>
Create your library
traffic = require "kamailio.traffic"
message = require "kamailio.message"
security = require "kamailio.security"
rex = require "rex_pcre"
local kamailio = {}
function kamailio:process_request()
[..]
end
function kamailio:process_reply()
[..]
end
return kamailio
Create your library
traffic = require "kamailio.traffic"
message = require "kamailio.message"
security = require "kamailio.security"
rex = require "rex_pcre"
local kamailio = {}
function kamailio:process_request()
[..]
end
function kamailio:process_reply()
[..]
end
return kamailio
Write your function
kamailio.cfg
route["clir"] {
if ($rU =~ "^*31[0-9]+$") {
strip(3);
setflag(21);
} else if ($rU =~ "^*31[*#][0-9]+$") {
strip(4);
setflag(21);
} else if ($rU =~ "^*31%23[0-9]+$") {
strip(6);
setflag(21);
}
if (isflagset(21) && method=="INVITE") {
xlog("L_NOTICE", "User wants to suppress his number for this call. F=$fU T=$tU
D=$fnn");
append_hf("Privacy: idrn");
}
}
Write your function
kamailio/headers.lua
rex = require "rex_pcre"
message = require "kamailio.message"
local headers = {}
function headers.get_request_user()
return KSR.pv.get("$rU")
end
function headers.set_request_user(value)
KSR.pv.sets("$rU", value)
end
function headers.set_privacy_header()
KSR.hdr.append("Privacy: id")
end
function headers.suppress_cid_if_needed()
request_user = headers.get_request_user()
if rex.find(request_user, "^*31([#]|%23)?") then
headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", ""))
if message.is_invite() then headers.set_privacy_header() end
end
end
return headers
Write your function
kamailio/headers.lua
rex = require "rex_pcre"
message = require "kamailio.message"
local headers = {}
function headers.get_request_user()
return KSR.pv.get("$rU")
end
function headers.set_request_user(value)
KSR.pv.sets("$rU", value)
end
function headers.set_privacy_header()
KSR.hdr.append("Privacy: id")
end
function headers.suppress_cid_if_needed()
request_user = headers.get_request_user()
if rex.find(request_user, "^*31([#]|%23)?") then
headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", ""))
if message.is_invite() then headers.set_privacy_header() end
end
end
return headers
Write your function
kamailio/headers.lua
rex = require "rex_pcre"
message = require "kamailio.message"
local headers = {}
function headers.get_request_user()
return KSR.pv.get("$rU")
end
function headers.set_request_user(value)
KSR.pv.sets("$rU", value)
end
function headers.set_privacy_header()
KSR.hdr.append("Privacy: id")
end
function headers.suppress_cid_if_needed()
request_user = headers.get_request_user()
if rex.find(request_user, "^*31([#]|%23)?") then
headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", ""))
if message.is_invite() then headers.set_privacy_header() end
end
end
return headers
Write your function
kamailio/headers.lua
rex = require "rex_pcre"
message = require "kamailio.message"
local headers = {}
function headers.get_request_user()
return KSR.pv.get("$rU")
end
function headers.set_request_user(value)
KSR.pv.sets("$rU", value)
end
function headers.set_privacy_header()
KSR.hdr.append("Privacy: id")
end
function headers.suppress_cid_if_needed()
request_user = headers.get_request_user()
if rex.find(request_user, "^*31([#]|%23)?") then
headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", ""))
if message.is_invite() then headers.set_privacy_header() end
end
end
return headers
Test it
spec/headers_spec.lua
require 'busted.runner'()
local headers = require "../kamailio/headers"
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function()
-- Call the function
headers.suppress_cid_if_needed()
-- Now check if everything is as expected
assert.spy(KSR.pv.sets).was.called_with( "$rU", "021112345" )
assert.spy(KSR.hdr.append).was.called_with("Privacy: id")
end)
end)
Test it
spec/headers_spec.lua
require 'busted.runner'()
local headers = require "../kamailio/headers"
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function()
-- Call the function
headers.suppress_cid_if_needed()
-- Now check if everything is as expected
assert.spy(KSR.pv.sets).was.called_with( "$rU", "021112345" )
assert.spy(KSR.hdr.append).was.called_with("Privacy: id")
end)
end)
Test it
spec/headers_spec.lua
require 'busted.runner'()
local headers = require "../kamailio/headers"
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function()
-- Call the function
headers.suppress_cid_if_needed()
-- Now check if everything is as expected
assert.spy(KSR.pv.sets).was.called_with( "$rU", "021112345" )
assert.spy(KSR.hdr.append).was.called_with("Privacy: id")
end)
end)
Test it
spec/headers_spec.lua
require 'busted.runner'()
local headers = require "../kamailio/headers"
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function()
-- Call the function
headers.suppress_cid_if_needed()
-- Now check if everything is as expected
assert.spy(KSR.pv.sets).was.called_with( "$rU", "021112345" )
assert.spy(KSR.hdr.append).was.called_with("Privacy: id")
end)
end)
Fix your tests
require 'busted.runner'()
local function init_mock(options)
-- mock global variable 'KSR'
local ksr_mock = {
pv = {
get = function(key)
if key == "$rU" then
if options.rU ~= nil then return options.rU else return "01234567" end
end
end,
sets = function(k, v) end,
},
hdr = {
append = function(header) end,
}
}
_G["KSR"] = mock(ksr_mock)
local message_mock = {
is_invite = function() return options.is_invite end
}
_G["message"] = mock(message_mock)
end
local headers = require "kamailio/headers"
[..]
Fix your tests
require 'busted.runner'()
local function init_mock(options)
-- mock global variable 'KSR'
local ksr_mock = {
pv = {
get = function(key)
if key == "$rU" then
if options.rU ~= nil then return options.rU else return "01234567" end
end
end,
sets = function(k, v) end,
},
hdr = {
append = function(header) end,
}
}
_G["KSR"] = mock(ksr_mock)
local message_mock = {
is_invite = function() return options.is_invite end
}
_G["message"] = mock(message_mock)
end
local headers = require "kamailio/headers"
[..]
Fix your tests
require 'busted.runner'()
local function init_mock(options)
-- mock global variable 'KSR'
local ksr_mock = {
pv = {
get = function(key)
if key == "$rU" then
if options.rU ~= nil then return options.rU else return "01234567" end
end
end,
sets = function(k, v) end,
},
hdr = {
append = function(header) end,
}
}
_G["KSR"] = mock(ksr_mock)
local message_mock = {
is_invite = function() return options.is_invite end
}
_G["message"] = mock(message_mock)
end
local headers = require "kamailio/headers"
[..]
Fix your tests
require 'busted.runner'()
local function init_mock(options) [..] end
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function()
-- Initialize the mock
init_mock{ rU = "*31021112345", is_invite = true }
-- Call the function
headers.suppress_cid_if_needed()
-- Now check if everything is as expected
assert.spy(KSR.pv.sets).was.called_with("$rU", "021112345")
assert.spy(KSR.hdr.append).was.called_with("Privacy: id")
end)
end)
Fix your tests
require 'busted.runner'()
local function init_mock(options) [..] end
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function()
-- Initialize the mock
init_mock{ rU = "*31021112345", is_invite = true }
-- Call the function
headers.suppress_cid_if_needed()
-- Now check if everything is as expected
assert.spy(KSR.pv.sets).was.called_with("$rU", "021112345")
assert.spy(KSR.hdr.append).was.called_with("Privacy: id")
end)
end)
Fix your tests
require 'busted.runner'()
local function init_mock(options) [..] end
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function()
-- Initialize the mock
init_mock{ rU = "*31021112345", is_invite = true }
-- Call the function
headers.suppress_cid_if_needed()
-- Now check if everything is as expected
assert.spy(KSR.pv.sets).was.called_with("$rU", "021112345")
assert.spy(KSR.hdr.append).was.called_with("Privacy: id")
end)
end)
Add more tests
require 'busted.runner'()
local function init_mock(options) [..] end
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function() .. end)
it("Caller dials *31*021112345", function() .. end)
it("Caller dials *31#021112345", function() .. end)
it("Caller dials *31%23021112345", function() .. end)
it("Caller dials +4921112345", function() .. end)
it("Caller dials *31+4921112345, non-INVITE", function() .. end)
end)
Add more tests
require 'busted.runner'()
local function init_mock(options) [..] end
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function() .. end)
it("Caller dials *31*021112345", function() .. end)
it("Caller dials *31#021112345", function() .. end)
it("Caller dials *31%23021112345", function() .. end)
it("Caller dials +4921112345", function()
init_mock{ rU = "+4921112345", is_invite = true }
headers.suppress_cid_if_needed()
assert.spy(KSR.pv.sets).was.called(0)
assert.spy(KSR.hdr.append).was.called(0)
end)
it("Caller dials *31+4921112345, non-INVITE", function()
init_mock{ rU = "*31+4921112345" }
headers.suppress_cid_if_needed()
assert.spy(KSR.pv.sets).was.called_with("$rU", "+4921112345")
assert.spy(KSR.hdr.append).was.called(0)
end)
end)
Add more tests
require 'busted.runner'()
local function init_mock(options) [..] end
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function() .. end)
it("Caller dials *31*021112345", function() .. end)
it("Caller dials *31#021112345", function() .. end)
it("Caller dials *31%23021112345", function() .. end)
it("Caller dials +4921112345", function()
init_mock{ rU = "+4921112345", is_invite = true }
headers.suppress_cid_if_needed()
assert.spy(KSR.pv.sets).was.called(0)
assert.spy(KSR.hdr.append).was.called(0)
end)
it("Caller dials *31+4921112345, non-INVITE", function()
init_mock{ rU = "*31+4921112345" }
headers.suppress_cid_if_needed()
assert.spy(KSR.pv.sets).was.called_with("$rU", "+4921112345")
assert.spy(KSR.hdr.append).was.called(0)
end)
end)
Add more tests
require 'busted.runner'()
local function init_mock(options) [..] end
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function() .. end)
it("Caller dials *31*021112345", function() .. end)
it("Caller dials *31#021112345", function() .. end)
it("Caller dials *31%23021112345", function() .. end)
it("Caller dials +4921112345", function()
init_mock{ rU = "+4921112345", is_invite = true }
headers.suppress_cid_if_needed()
assert.spy(KSR.pv.sets).was.called(0)
assert.spy(KSR.hdr.append).was.called(0)
end)
it("Caller dials *31+4921112345, non-INVITE", function()
init_mock{ rU = "*31+4921112345" }
headers.suppress_cid_if_needed()
assert.spy(KSR.pv.sets).was.called_with("$rU", "+4921112345")
assert.spy(KSR.hdr.append).was.called(0)
end)
end)
Add more tests
require 'busted.runner'()
local function init_mock(options) [..] end
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function() .. end)
it("Caller dials *31*021112345", function() .. end)
it("Caller dials *31#021112345", function() .. end)
it("Caller dials *31%23021112345", function() .. end)
it("Caller dials +4921112345", function()
init_mock{ rU = "+4921112345", is_invite = true }
headers.suppress_cid_if_needed()
assert.spy(KSR.pv.sets).was.called(0)
assert.spy(KSR.hdr.append).was.called(0)
end)
it("Caller dials *31+4921112345, non-INVITE", function()
init_mock{ rU = "*31+4921112345" }
headers.suppress_cid_if_needed()
assert.spy(KSR.pv.sets).was.called_with("$rU", "+4921112345")
assert.spy(KSR.hdr.append).was.called(0)
end)
end)
Fix your code
kamailio/headers.lua
rex = require "rex_pcre"
message = require "kamailio.message"
local headers = {}
function headers.get_request_user()
return KSR.pv.get("$rU")
end
function headers.set_request_user(value)
KSR.pv.sets("$rU", value)
end
function headers.set_privacy_header()
KSR.hdr.append("Privacy: id")
end
function headers.suppress_cid_if_needed()
request_user = headers.get_request_user()
if rex.find(request_user, "^*31([#]|%23)?") then
headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", ""))
if message.is_invite() then headers.set_privacy_header() end
end
end
return headers
Fix your code
kamailio/headers.lua
rex = require "rex_pcre"
message = require "kamailio.message"
local headers = {}
function headers.get_request_user()
return KSR.pv.get("$rU")
end
function headers.set_request_user(value)
KSR.pv.sets("$rU", value)
end
function headers.set_privacy_header()
KSR.hdr.append("Privacy: id")
end
function headers.suppress_cid_if_needed()
request_user = headers.get_request_user()
if rex.find(request_user, "^*31([#]|%23)?") then
headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", ""))
if message.is_invite() then headers.set_privacy_header() end
end
end
return headers
Fix your code
kamailio/headers.lua
rex = require "rex_pcre"
message = require "kamailio.message"
local headers = {}
function headers.get_request_user()
return KSR.pv.get("$rU")
end
function headers.set_request_user(value)
KSR.pv.sets("$rU", value)
end
function headers.set_privacy_header()
KSR.hdr.append("Privacy: id")
end
function headers.suppress_cid_if_needed()
request_user = headers.get_request_user()
if rex.find(request_user, "^*31([*#]|%23)?") then
headers.set_request_user(rex.gsub(request_user, "^*31([*#]|%23)?", ""))
if message.is_invite() then headers.set_privacy_header() end
end
end
return headers
Fix your code
kamailio/headers.lua
rex = require "rex_pcre"
message = require "kamailio.message"
local headers = {}
function headers.get_request_user()
return KSR.pv.get("$rU")
end
function headers.set_request_user(value)
KSR.pv.sets("$rU", value)
end
function headers.set_privacy_header()
KSR.hdr.append("Privacy: id")
end
function headers.suppress_cid_if_needed()
request_user = headers.get_request_user()
if rex.find(request_user, "^*31([*#]|%23)?") then
headers.set_request_user(rex.gsub(request_user, "^*31([*#]|%23)?", ""))
if message.is_invite() then headers.set_privacy_header() end
end
end
return headers
Rerun your tests
require 'busted.runner'()
local function init_mock(options) [..] end
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function() .. end)
it("Caller dials *31*021112345", function() .. end)
it("Caller dials *31#021112345", function() .. end)
it("Caller dials *31%23021112345", function() .. end)
it("Caller dials +4921112345", function() .. end)
it("Caller dials *31+4921112345, non-INVITE", function() .. end)
end)
Rerun your tests
require 'busted.runner'()
local function init_mock(options) [..] end
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function() .. end)
it("Caller dials *31*021112345", function() .. end)
it("Caller dials *31#021112345", function() .. end)
it("Caller dials *31%23021112345", function() .. end)
it("Caller dials +4921112345", function() .. end)
it("Caller dials *31+4921112345, non-INVITE", function() .. end)
end)
Too dry?
Try it yourself:
https://github.com/sipgate/lua-kamailio
That's it
Questions?
Want to chat? Stop by!

More Related Content

What's hot

Introduction to cloudforecast
Introduction to cloudforecastIntroduction to cloudforecast
Introduction to cloudforecast
Masahiro Nagano
 
How DSL works on Ruby
How DSL works on RubyHow DSL works on Ruby
How DSL works on Ruby
Hiroshi SHIBATA
 
Kamailio - Surfing Big Waves Of SIP With Style
Kamailio - Surfing Big Waves Of SIP With StyleKamailio - Surfing Big Waves Of SIP With Style
Kamailio - Surfing Big Waves Of SIP With Style
Daniel-Constantin Mierla
 
FOSDEM 2017 - RTC Services With Lua and Kamailio
FOSDEM 2017 - RTC Services With Lua and KamailioFOSDEM 2017 - RTC Services With Lua and Kamailio
FOSDEM 2017 - RTC Services With Lua and Kamailio
Daniel-Constantin Mierla
 
Minimal MVC in JavaScript
Minimal MVC in JavaScriptMinimal MVC in JavaScript
Minimal MVC in JavaScript
Mosky Liu
 
Concurrency in Python
Concurrency in PythonConcurrency in Python
Concurrency in Python
Mosky Liu
 
The Year of JRuby - RubyC 2018
The Year of JRuby - RubyC 2018The Year of JRuby - RubyC 2018
The Year of JRuby - RubyC 2018
Charles Nutter
 
Devel::NYTProf v5 at YAPC::NA 201406
Devel::NYTProf v5 at YAPC::NA 201406Devel::NYTProf v5 at YAPC::NA 201406
Devel::NYTProf v5 at YAPC::NA 201406
Tim Bunce
 
Perl Memory Use 201207 (OUTDATED, see 201209 )
Perl Memory Use 201207 (OUTDATED, see 201209 )Perl Memory Use 201207 (OUTDATED, see 201209 )
Perl Memory Use 201207 (OUTDATED, see 201209 )
Tim Bunce
 
Fisl - Deployment
Fisl - DeploymentFisl - Deployment
Fisl - Deployment
Fabio Akita
 
Gemification for Ruby 2.5/3.0
Gemification for Ruby 2.5/3.0Gemification for Ruby 2.5/3.0
Gemification for Ruby 2.5/3.0
Hiroshi SHIBATA
 
Troubleshooting common oslo.messaging and RabbitMQ issues
Troubleshooting common oslo.messaging and RabbitMQ issuesTroubleshooting common oslo.messaging and RabbitMQ issues
Troubleshooting common oslo.messaging and RabbitMQ issues
Michael Klishin
 
typemap in Perl/XS
typemap in Perl/XS  typemap in Perl/XS
typemap in Perl/XS
charsbar
 
Kamailio Updates - VUC 588
Kamailio Updates - VUC 588Kamailio Updates - VUC 588
Kamailio Updates - VUC 588
Daniel-Constantin Mierla
 
Roll Your Own API Management Platform with nginx and Lua
Roll Your Own API Management Platform with nginx and LuaRoll Your Own API Management Platform with nginx and Lua
Roll Your Own API Management Platform with nginx and Lua
Jon Moore
 
Kamailio - SIP Firewall for Carrier Grade Traffic
Kamailio - SIP Firewall for Carrier Grade TrafficKamailio - SIP Firewall for Carrier Grade Traffic
Kamailio - SIP Firewall for Carrier Grade Traffic
Daniel-Constantin Mierla
 
Using ngx_lua in UPYUN
Using ngx_lua in UPYUNUsing ngx_lua in UPYUN
Using ngx_lua in UPYUN
Cong Zhang
 
PuppetConf 2017: What's in a Name? Scaling ENC with DNS- Cameron Nicholson, A...
PuppetConf 2017: What's in a Name? Scaling ENC with DNS- Cameron Nicholson, A...PuppetConf 2017: What's in a Name? Scaling ENC with DNS- Cameron Nicholson, A...
PuppetConf 2017: What's in a Name? Scaling ENC with DNS- Cameron Nicholson, A...
Puppet
 
Lua tech talk
Lua tech talkLua tech talk
Lua tech talk
Locaweb
 
Using Kamailio for Scalability and Security
Using Kamailio for Scalability and SecurityUsing Kamailio for Scalability and Security
Using Kamailio for Scalability and Security
Fred Posner
 

What's hot (20)

Introduction to cloudforecast
Introduction to cloudforecastIntroduction to cloudforecast
Introduction to cloudforecast
 
How DSL works on Ruby
How DSL works on RubyHow DSL works on Ruby
How DSL works on Ruby
 
Kamailio - Surfing Big Waves Of SIP With Style
Kamailio - Surfing Big Waves Of SIP With StyleKamailio - Surfing Big Waves Of SIP With Style
Kamailio - Surfing Big Waves Of SIP With Style
 
FOSDEM 2017 - RTC Services With Lua and Kamailio
FOSDEM 2017 - RTC Services With Lua and KamailioFOSDEM 2017 - RTC Services With Lua and Kamailio
FOSDEM 2017 - RTC Services With Lua and Kamailio
 
Minimal MVC in JavaScript
Minimal MVC in JavaScriptMinimal MVC in JavaScript
Minimal MVC in JavaScript
 
Concurrency in Python
Concurrency in PythonConcurrency in Python
Concurrency in Python
 
The Year of JRuby - RubyC 2018
The Year of JRuby - RubyC 2018The Year of JRuby - RubyC 2018
The Year of JRuby - RubyC 2018
 
Devel::NYTProf v5 at YAPC::NA 201406
Devel::NYTProf v5 at YAPC::NA 201406Devel::NYTProf v5 at YAPC::NA 201406
Devel::NYTProf v5 at YAPC::NA 201406
 
Perl Memory Use 201207 (OUTDATED, see 201209 )
Perl Memory Use 201207 (OUTDATED, see 201209 )Perl Memory Use 201207 (OUTDATED, see 201209 )
Perl Memory Use 201207 (OUTDATED, see 201209 )
 
Fisl - Deployment
Fisl - DeploymentFisl - Deployment
Fisl - Deployment
 
Gemification for Ruby 2.5/3.0
Gemification for Ruby 2.5/3.0Gemification for Ruby 2.5/3.0
Gemification for Ruby 2.5/3.0
 
Troubleshooting common oslo.messaging and RabbitMQ issues
Troubleshooting common oslo.messaging and RabbitMQ issuesTroubleshooting common oslo.messaging and RabbitMQ issues
Troubleshooting common oslo.messaging and RabbitMQ issues
 
typemap in Perl/XS
typemap in Perl/XS  typemap in Perl/XS
typemap in Perl/XS
 
Kamailio Updates - VUC 588
Kamailio Updates - VUC 588Kamailio Updates - VUC 588
Kamailio Updates - VUC 588
 
Roll Your Own API Management Platform with nginx and Lua
Roll Your Own API Management Platform with nginx and LuaRoll Your Own API Management Platform with nginx and Lua
Roll Your Own API Management Platform with nginx and Lua
 
Kamailio - SIP Firewall for Carrier Grade Traffic
Kamailio - SIP Firewall for Carrier Grade TrafficKamailio - SIP Firewall for Carrier Grade Traffic
Kamailio - SIP Firewall for Carrier Grade Traffic
 
Using ngx_lua in UPYUN
Using ngx_lua in UPYUNUsing ngx_lua in UPYUN
Using ngx_lua in UPYUN
 
PuppetConf 2017: What's in a Name? Scaling ENC with DNS- Cameron Nicholson, A...
PuppetConf 2017: What's in a Name? Scaling ENC with DNS- Cameron Nicholson, A...PuppetConf 2017: What's in a Name? Scaling ENC with DNS- Cameron Nicholson, A...
PuppetConf 2017: What's in a Name? Scaling ENC with DNS- Cameron Nicholson, A...
 
Lua tech talk
Lua tech talkLua tech talk
Lua tech talk
 
Using Kamailio for Scalability and Security
Using Kamailio for Scalability and SecurityUsing Kamailio for Scalability and Security
Using Kamailio for Scalability and Security
 

Similar to Kamailioworld 2018 - Modular and test driven SIP Routing with Lua

Kamailio - Unifying SIP and Web Worlds with Lua
Kamailio - Unifying SIP and Web Worlds with LuaKamailio - Unifying SIP and Web Worlds with Lua
Kamailio - Unifying SIP and Web Worlds with Lua
Daniel-Constantin Mierla
 
PuppetDB: Sneaking Clojure into Operations
PuppetDB: Sneaking Clojure into OperationsPuppetDB: Sneaking Clojure into Operations
PuppetDB: Sneaking Clojure into Operationsgrim_radical
 
From SaltStack to Puppet and beyond...
From SaltStack to Puppet and beyond...From SaltStack to Puppet and beyond...
From SaltStack to Puppet and beyond...
Yury Bushmelev
 
#WeSpeakLinux Session
#WeSpeakLinux Session#WeSpeakLinux Session
#WeSpeakLinux Session
Kellyn Pot'Vin-Gorman
 
Ingesting hdfs intosolrusingsparktrimmed
Ingesting hdfs intosolrusingsparktrimmedIngesting hdfs intosolrusingsparktrimmed
Ingesting hdfs intosolrusingsparktrimmed
whoschek
 
Drupal Efficiency - Coding, Deployment, Scaling
Drupal Efficiency - Coding, Deployment, ScalingDrupal Efficiency - Coding, Deployment, Scaling
Drupal Efficiency - Coding, Deployment, Scaling
smattoon
 
Ansible - A 'crowd' introduction
Ansible - A 'crowd' introductionAnsible - A 'crowd' introduction
Ansible - A 'crowd' introduction
Manuel de la Peña Peña
 
Why Managed Service Providers Should Embrace Container Technology
Why Managed Service Providers Should Embrace Container TechnologyWhy Managed Service Providers Should Embrace Container Technology
Why Managed Service Providers Should Embrace Container Technology
Sagi Brody
 
Drupal Efficiency using open source technologies from Sun
Drupal Efficiency using open source technologies from SunDrupal Efficiency using open source technologies from Sun
Drupal Efficiency using open source technologies from Sun
smattoon
 
Slackware Demystified [SELF 2011]
Slackware Demystified [SELF 2011]Slackware Demystified [SELF 2011]
Slackware Demystified [SELF 2011]
Vincent Batts
 
Vorontsov, golovko ssrf attacks and sockets. smorgasbord of vulnerabilities
Vorontsov, golovko   ssrf attacks and sockets. smorgasbord of vulnerabilitiesVorontsov, golovko   ssrf attacks and sockets. smorgasbord of vulnerabilities
Vorontsov, golovko ssrf attacks and sockets. smorgasbord of vulnerabilitiesDefconRussia
 
Logstash
LogstashLogstash
Logstash
琛琳 饶
 
JavaOne 2010: Top 10 Causes for Java Issues in Production and What to Do When...
JavaOne 2010: Top 10 Causes for Java Issues in Production and What to Do When...JavaOne 2010: Top 10 Causes for Java Issues in Production and What to Do When...
JavaOne 2010: Top 10 Causes for Java Issues in Production and What to Do When...
srisatish ambati
 
Kamailio with Docker and Kubernetes
Kamailio with Docker and KubernetesKamailio with Docker and Kubernetes
Kamailio with Docker and Kubernetes
Paolo Visintin
 
Kamailio - SIP Routing in Lua
Kamailio - SIP Routing in LuaKamailio - SIP Routing in Lua
Kamailio - SIP Routing in Lua
Daniel-Constantin Mierla
 
How to automate all your SEO projects
How to automate all your SEO projectsHow to automate all your SEO projects
How to automate all your SEO projects
Vincent Terrasi
 
Migrating to XtraDB Cluster
Migrating to XtraDB ClusterMigrating to XtraDB Cluster
Migrating to XtraDB Cluster
percona2013
 
Docker
DockerDocker
Docker
Chen Chun
 
Kafka meetup JP #3 - Engineering Apache Kafka at LINE
Kafka meetup JP #3 - Engineering Apache Kafka at LINEKafka meetup JP #3 - Engineering Apache Kafka at LINE
Kafka meetup JP #3 - Engineering Apache Kafka at LINE
kawamuray
 
Fault Tolerance in Spark: Lessons Learned from Production: Spark Summit East ...
Fault Tolerance in Spark: Lessons Learned from Production: Spark Summit East ...Fault Tolerance in Spark: Lessons Learned from Production: Spark Summit East ...
Fault Tolerance in Spark: Lessons Learned from Production: Spark Summit East ...
Spark Summit
 

Similar to Kamailioworld 2018 - Modular and test driven SIP Routing with Lua (20)

Kamailio - Unifying SIP and Web Worlds with Lua
Kamailio - Unifying SIP and Web Worlds with LuaKamailio - Unifying SIP and Web Worlds with Lua
Kamailio - Unifying SIP and Web Worlds with Lua
 
PuppetDB: Sneaking Clojure into Operations
PuppetDB: Sneaking Clojure into OperationsPuppetDB: Sneaking Clojure into Operations
PuppetDB: Sneaking Clojure into Operations
 
From SaltStack to Puppet and beyond...
From SaltStack to Puppet and beyond...From SaltStack to Puppet and beyond...
From SaltStack to Puppet and beyond...
 
#WeSpeakLinux Session
#WeSpeakLinux Session#WeSpeakLinux Session
#WeSpeakLinux Session
 
Ingesting hdfs intosolrusingsparktrimmed
Ingesting hdfs intosolrusingsparktrimmedIngesting hdfs intosolrusingsparktrimmed
Ingesting hdfs intosolrusingsparktrimmed
 
Drupal Efficiency - Coding, Deployment, Scaling
Drupal Efficiency - Coding, Deployment, ScalingDrupal Efficiency - Coding, Deployment, Scaling
Drupal Efficiency - Coding, Deployment, Scaling
 
Ansible - A 'crowd' introduction
Ansible - A 'crowd' introductionAnsible - A 'crowd' introduction
Ansible - A 'crowd' introduction
 
Why Managed Service Providers Should Embrace Container Technology
Why Managed Service Providers Should Embrace Container TechnologyWhy Managed Service Providers Should Embrace Container Technology
Why Managed Service Providers Should Embrace Container Technology
 
Drupal Efficiency using open source technologies from Sun
Drupal Efficiency using open source technologies from SunDrupal Efficiency using open source technologies from Sun
Drupal Efficiency using open source technologies from Sun
 
Slackware Demystified [SELF 2011]
Slackware Demystified [SELF 2011]Slackware Demystified [SELF 2011]
Slackware Demystified [SELF 2011]
 
Vorontsov, golovko ssrf attacks and sockets. smorgasbord of vulnerabilities
Vorontsov, golovko   ssrf attacks and sockets. smorgasbord of vulnerabilitiesVorontsov, golovko   ssrf attacks and sockets. smorgasbord of vulnerabilities
Vorontsov, golovko ssrf attacks and sockets. smorgasbord of vulnerabilities
 
Logstash
LogstashLogstash
Logstash
 
JavaOne 2010: Top 10 Causes for Java Issues in Production and What to Do When...
JavaOne 2010: Top 10 Causes for Java Issues in Production and What to Do When...JavaOne 2010: Top 10 Causes for Java Issues in Production and What to Do When...
JavaOne 2010: Top 10 Causes for Java Issues in Production and What to Do When...
 
Kamailio with Docker and Kubernetes
Kamailio with Docker and KubernetesKamailio with Docker and Kubernetes
Kamailio with Docker and Kubernetes
 
Kamailio - SIP Routing in Lua
Kamailio - SIP Routing in LuaKamailio - SIP Routing in Lua
Kamailio - SIP Routing in Lua
 
How to automate all your SEO projects
How to automate all your SEO projectsHow to automate all your SEO projects
How to automate all your SEO projects
 
Migrating to XtraDB Cluster
Migrating to XtraDB ClusterMigrating to XtraDB Cluster
Migrating to XtraDB Cluster
 
Docker
DockerDocker
Docker
 
Kafka meetup JP #3 - Engineering Apache Kafka at LINE
Kafka meetup JP #3 - Engineering Apache Kafka at LINEKafka meetup JP #3 - Engineering Apache Kafka at LINE
Kafka meetup JP #3 - Engineering Apache Kafka at LINE
 
Fault Tolerance in Spark: Lessons Learned from Production: Spark Summit East ...
Fault Tolerance in Spark: Lessons Learned from Production: Spark Summit East ...Fault Tolerance in Spark: Lessons Learned from Production: Spark Summit East ...
Fault Tolerance in Spark: Lessons Learned from Production: Spark Summit East ...
 

Recently uploaded

Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Globus
 
Top Features to Include in Your Winzo Clone App for Business Growth (4).pptx
Top Features to Include in Your Winzo Clone App for Business Growth (4).pptxTop Features to Include in Your Winzo Clone App for Business Growth (4).pptx
Top Features to Include in Your Winzo Clone App for Business Growth (4).pptx
rickgrimesss22
 
Enhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdfEnhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdf
Globus
 
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Mind IT Systems
 
AI Pilot Review: The World’s First Virtual Assistant Marketing Suite
AI Pilot Review: The World’s First Virtual Assistant Marketing SuiteAI Pilot Review: The World’s First Virtual Assistant Marketing Suite
AI Pilot Review: The World’s First Virtual Assistant Marketing Suite
Google
 
How to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good PracticesHow to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good Practices
Globus
 
top nidhi software solution freedownload
top nidhi software solution freedownloadtop nidhi software solution freedownload
top nidhi software solution freedownload
vrstrong314
 
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Anthony Dahanne
 
May Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdfMay Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdf
Adele Miller
 
GlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote sessionGlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote session
Globus
 
Globus Compute Introduction - GlobusWorld 2024
Globus Compute Introduction - GlobusWorld 2024Globus Compute Introduction - GlobusWorld 2024
Globus Compute Introduction - GlobusWorld 2024
Globus
 
Understanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSageUnderstanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSage
Globus
 
BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024
Ortus Solutions, Corp
 
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data AnalysisProviding Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
Globus
 
Enterprise Resource Planning System in Telangana
Enterprise Resource Planning System in TelanganaEnterprise Resource Planning System in Telangana
Enterprise Resource Planning System in Telangana
NYGGS Automation Suite
 
First Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User EndpointsFirst Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User Endpoints
Globus
 
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar
 
Using IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New ZealandUsing IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New Zealand
IES VE
 
Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...
Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...
Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...
Shahin Sheidaei
 
Developing Distributed High-performance Computing Capabilities of an Open Sci...
Developing Distributed High-performance Computing Capabilities of an Open Sci...Developing Distributed High-performance Computing Capabilities of an Open Sci...
Developing Distributed High-performance Computing Capabilities of an Open Sci...
Globus
 

Recently uploaded (20)

Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
 
Top Features to Include in Your Winzo Clone App for Business Growth (4).pptx
Top Features to Include in Your Winzo Clone App for Business Growth (4).pptxTop Features to Include in Your Winzo Clone App for Business Growth (4).pptx
Top Features to Include in Your Winzo Clone App for Business Growth (4).pptx
 
Enhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdfEnhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdf
 
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
 
AI Pilot Review: The World’s First Virtual Assistant Marketing Suite
AI Pilot Review: The World’s First Virtual Assistant Marketing SuiteAI Pilot Review: The World’s First Virtual Assistant Marketing Suite
AI Pilot Review: The World’s First Virtual Assistant Marketing Suite
 
How to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good PracticesHow to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good Practices
 
top nidhi software solution freedownload
top nidhi software solution freedownloadtop nidhi software solution freedownload
top nidhi software solution freedownload
 
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
 
May Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdfMay Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdf
 
GlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote sessionGlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote session
 
Globus Compute Introduction - GlobusWorld 2024
Globus Compute Introduction - GlobusWorld 2024Globus Compute Introduction - GlobusWorld 2024
Globus Compute Introduction - GlobusWorld 2024
 
Understanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSageUnderstanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSage
 
BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024
 
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data AnalysisProviding Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
 
Enterprise Resource Planning System in Telangana
Enterprise Resource Planning System in TelanganaEnterprise Resource Planning System in Telangana
Enterprise Resource Planning System in Telangana
 
First Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User EndpointsFirst Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User Endpoints
 
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBroker
 
Using IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New ZealandUsing IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New Zealand
 
Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...
Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...
Gamify Your Mind; The Secret Sauce to Delivering Success, Continuously Improv...
 
Developing Distributed High-performance Computing Capabilities of an Open Sci...
Developing Distributed High-performance Computing Capabilities of an Open Sci...Developing Distributed High-performance Computing Capabilities of an Open Sci...
Developing Distributed High-performance Computing Capabilities of an Open Sci...
 

Kamailioworld 2018 - Modular and test driven SIP Routing with Lua

  • 1. Sebastian Damm E: damm@sipgate.de T: @_SebastianDamm Modular And Test Driven SIP Routing With Lua
  • 2. Who we are, what we do • Düsseldorf based VoIP service provider (since 2004) • Active in Germany and UK • Full MVNO in the Telefónica network • Private and Business customers • VoIP and Mobile products • Some 100k active customers • Almost 100 million minutes each month
  • 3. Of course, we use Kamailio
  • 4. Of course, we use Kamailio All over our network.
  • 5. Of course, we use Kamailio All over our network. But we have a huge problem!
  • 6. What problem? at sipgate • methods of software development have changed: - Agile, fast cycles, many releases - small modules - unit tests, continuous deployment - permanent refactoring of functions
  • 7. What problem? at sipgate • methods of software development have changed: - Agile, fast cycles, many releases - small modules - unit tests, continuous deployment - permanent refactoring of functions • telephony side: - "organically grown" routing logic (since 2004) - 40 developers, but only 4 people can read/write it - learnings from software development haven't made it to this part
  • 18. But how do we fix that?
  • 19. But how do we fix that? • app_*: - app_lua - app_python - app_jsdt - app_java, app_mono, app_perl - missing: app_ruby ;)
  • 20. But how do we fix that? • app_*: - app_lua - app_python - app_jsdt - app_java, app_mono, app_perl - missing: app_ruby ;) • KEMI - lua - python - jsdt - sqlang
  • 21. Why Lua? 1. Speed Interpreter Average Min Max Native 302.28 6 3824 Lua 308.32 6 3596 Python 393.71 23 3266 All times in Microseconds. Source: https://www.kamailio.org/wiki/devel/config-engines#interpreters_performances
  • 22. Why Lua? 2. Easy to learn • None of us had written Lua (production) code before. • If you've written code before, you'll be able to start right away. • Less fear of changing something within Kamailio.
  • 23. Why Lua? 3. You can work test-driven • Impossible with native Kamailio language • Know if you broke something before you deploy it!
  • 24. Standard way: one big Lua file Still many lines of code
  • 25. Standard way: one big Lua file Still have to know Kamailio syntax all the time function ksr_request_route() -- per request initial checks ksr_route_reqinit(); -- NAT detection ksr_route_natdetect(); -- CANCEL processing if KSR.pv.get("$rm") == "CANCEL" then if KSR.tm.t_check_trans()>0 then ksr_route_relay(); end return 1; end -- handle requests within SIP dialogs ksr_route_withindlg(); -- -- only initial requests (no To tag) -- handle retransmissions if KSR.tmx.t_precheck_trans()>0 then KSR.tm.t_check_trans(); return 1; end if KSR.tm.t_check_trans()==0 then return 1 end -- authentication ksr_route_auth(); -- remove preloaded route headers KSR.hdr.remove("Route"); if string.find("INVITE|SUBSCRIBE", KSR.pv.get("$rm")) then KSR.rr.record_route(); end -- account only INVITEs if KSR.pv.get("$rm")=="INVITE" then KSR.setflag(FLT_ACC); -- do accounting end -- dispatch requests to foreign domains ksr_route_sipout(); -- -- requests for my local domains -- handle registrations ksr_route_registrar(); if KSR.pv.is_null("$rU") then -- request with no Username in RURI KSR.sl.sl_send_reply(484,"Address Incomplete"); return 1; end -- user location service ksr_route_location(); return 1; end
  • 26. Standard way: one big Lua file Still not testable function ksr_request_route() -- per request initial checks ksr_route_reqinit(); -- NAT detection ksr_route_natdetect(); -- CANCEL processing if KSR.pv.get("$rm") == "CANCEL" then if KSR.tm.t_check_trans()>0 then ksr_route_relay(); end return 1; end -- handle requests within SIP dialogs ksr_route_withindlg(); -- -- only initial requests (no To tag) -- handle retransmissions if KSR.tmx.t_precheck_trans()>0 then KSR.tm.t_check_trans(); return 1; end if KSR.tm.t_check_trans()==0 then return 1 end -- authentication ksr_route_auth(); -- remove preloaded route headers KSR.hdr.remove("Route"); if string.find("INVITE|SUBSCRIBE", KSR.pv.get("$rm")) then KSR.rr.record_route(); end -- account only INVITEs if KSR.pv.get("$rm")=="INVITE" then KSR.setflag(FLT_ACC); -- do accounting end -- dispatch requests to foreign domains ksr_route_sipout(); -- -- requests for my local domains -- handle registrations ksr_route_registrar(); if KSR.pv.is_null("$rU") then -- request with no Username in RURI KSR.sl.sl_send_reply(484,"Address Incomplete"); return 1; end -- user location service ksr_route_location(); return 1; end
  • 27. How about a library? • Encapsulate Kamailio functions in easy-to-use native functions • Re-use often used calls • Testable • Fix logic errors before deploying them into production if KSR.pv.get("$rm") == "INVITE" then [..] end if kamailio.is_invite() then [..] end
  • 28. How about a library? Reduce the main script to a minimum kamailio = require("kamailio") function ksr_request_route() kamailio.process_request() end
  • 29. So how do we get there? • Install dependencies - Interpreter: lua, version 5.1 (apt-get install lua-5.1) - Testing framework: busted (apt-get install lua-busted) • Create your library. • Define your function. • Write tests. • Make the function work.
  • 30. Create your library • Can be just one file containing all your methods
 /etc/kamailio/kamailio-functions.lua
 /usr/local/share/lua/5.1/kamailio-functions.lua
  • 31. Create your library • Can be just one file containing all your methods
 /etc/kamailio/kamailio-functions.lua
 /usr/local/share/lua/5.1/kamailio-functions.lua • Can be a directory containing multiple files
 /usr/local/share/lua/5.1/kamailio/
 !"" actions.lua
 !"" init.lua
 !"" message.lua
 !"" message_state.lua
 !"" nathandling.lua
 !"" security.lua
 #"" traffic.lua
  • 32. Create your library • Can be just one file containing all your methods
 /etc/kamailio/kamailio-functions.lua
 /usr/local/share/lua/5.1/kamailio-functions.lua • Can be a directory containing multiple files
 /usr/local/share/lua/5.1/kamailio/
 !"" actions.lua
 !"" init.lua
 !"" message.lua
 !"" message_state.lua
 !"" nathandling.lua
 !"" security.lua
 #"" traffic.lua
  • 33. Create your library • Can be just one file containing all your methods
 /etc/kamailio/kamailio-functions.lua
 /usr/local/share/lua/5.1/kamailio-functions.lua • Can be a directory containing multiple files
 /usr/local/share/lua/5.1/kamailio/
 !"" actions.lua
 !"" init.lua
 !"" message.lua
 !"" message_state.lua
 !"" nathandling.lua
 !"" security.lua
 #"" traffic.lua damm@stretch:~$ lua Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio > foo = require "foo" stdin:1: module 'foo' not found: no field package.preload['foo'] no file './foo.lua' no file '/usr/local/share/lua/5.1/foo.lua' no file '/usr/local/share/lua/5.1/foo/init.lua' no file '/usr/local/lib/lua/5.1/foo.lua' no file '/usr/local/lib/lua/5.1/foo/init.lua' no file '/usr/share/lua/5.1/foo.lua' no file '/usr/share/lua/5.1/foo/init.lua' no file './foo.so' no file '/usr/local/lib/lua/5.1/foo.so' no file '/usr/lib/x86_64-linux-gnu/lua/5.1/foo.so' no file '/usr/lib/lua/5.1/foo.so' no file '/usr/local/lib/lua/5.1/loadall.so' stack traceback: [C]: in function 'require' stdin:1: in main chunk [C]: ? >
  • 34. Create your library traffic = require "kamailio.traffic" message = require "kamailio.message" security = require "kamailio.security" rex = require "rex_pcre" local kamailio = {} function kamailio:process_request() [..] end function kamailio:process_reply() [..] end return kamailio
  • 35. Create your library traffic = require "kamailio.traffic" message = require "kamailio.message" security = require "kamailio.security" rex = require "rex_pcre" local kamailio = {} function kamailio:process_request() [..] end function kamailio:process_reply() [..] end return kamailio
  • 36. Write your function kamailio.cfg route["clir"] { if ($rU =~ "^*31[0-9]+$") { strip(3); setflag(21); } else if ($rU =~ "^*31[*#][0-9]+$") { strip(4); setflag(21); } else if ($rU =~ "^*31%23[0-9]+$") { strip(6); setflag(21); } if (isflagset(21) && method=="INVITE") { xlog("L_NOTICE", "User wants to suppress his number for this call. F=$fU T=$tU D=$fnn"); append_hf("Privacy: idrn"); } }
  • 37. Write your function kamailio/headers.lua rex = require "rex_pcre" message = require "kamailio.message" local headers = {} function headers.get_request_user() return KSR.pv.get("$rU") end function headers.set_request_user(value) KSR.pv.sets("$rU", value) end function headers.set_privacy_header() KSR.hdr.append("Privacy: id") end function headers.suppress_cid_if_needed() request_user = headers.get_request_user() if rex.find(request_user, "^*31([#]|%23)?") then headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", "")) if message.is_invite() then headers.set_privacy_header() end end end return headers
  • 38. Write your function kamailio/headers.lua rex = require "rex_pcre" message = require "kamailio.message" local headers = {} function headers.get_request_user() return KSR.pv.get("$rU") end function headers.set_request_user(value) KSR.pv.sets("$rU", value) end function headers.set_privacy_header() KSR.hdr.append("Privacy: id") end function headers.suppress_cid_if_needed() request_user = headers.get_request_user() if rex.find(request_user, "^*31([#]|%23)?") then headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", "")) if message.is_invite() then headers.set_privacy_header() end end end return headers
  • 39. Write your function kamailio/headers.lua rex = require "rex_pcre" message = require "kamailio.message" local headers = {} function headers.get_request_user() return KSR.pv.get("$rU") end function headers.set_request_user(value) KSR.pv.sets("$rU", value) end function headers.set_privacy_header() KSR.hdr.append("Privacy: id") end function headers.suppress_cid_if_needed() request_user = headers.get_request_user() if rex.find(request_user, "^*31([#]|%23)?") then headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", "")) if message.is_invite() then headers.set_privacy_header() end end end return headers
  • 40. Write your function kamailio/headers.lua rex = require "rex_pcre" message = require "kamailio.message" local headers = {} function headers.get_request_user() return KSR.pv.get("$rU") end function headers.set_request_user(value) KSR.pv.sets("$rU", value) end function headers.set_privacy_header() KSR.hdr.append("Privacy: id") end function headers.suppress_cid_if_needed() request_user = headers.get_request_user() if rex.find(request_user, "^*31([#]|%23)?") then headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", "")) if message.is_invite() then headers.set_privacy_header() end end end return headers
  • 41. Test it spec/headers_spec.lua require 'busted.runner'() local headers = require "../kamailio/headers" describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() -- Call the function headers.suppress_cid_if_needed() -- Now check if everything is as expected assert.spy(KSR.pv.sets).was.called_with( "$rU", "021112345" ) assert.spy(KSR.hdr.append).was.called_with("Privacy: id") end) end)
  • 42. Test it spec/headers_spec.lua require 'busted.runner'() local headers = require "../kamailio/headers" describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() -- Call the function headers.suppress_cid_if_needed() -- Now check if everything is as expected assert.spy(KSR.pv.sets).was.called_with( "$rU", "021112345" ) assert.spy(KSR.hdr.append).was.called_with("Privacy: id") end) end)
  • 43. Test it spec/headers_spec.lua require 'busted.runner'() local headers = require "../kamailio/headers" describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() -- Call the function headers.suppress_cid_if_needed() -- Now check if everything is as expected assert.spy(KSR.pv.sets).was.called_with( "$rU", "021112345" ) assert.spy(KSR.hdr.append).was.called_with("Privacy: id") end) end)
  • 44. Test it spec/headers_spec.lua require 'busted.runner'() local headers = require "../kamailio/headers" describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() -- Call the function headers.suppress_cid_if_needed() -- Now check if everything is as expected assert.spy(KSR.pv.sets).was.called_with( "$rU", "021112345" ) assert.spy(KSR.hdr.append).was.called_with("Privacy: id") end) end)
  • 45. Fix your tests require 'busted.runner'() local function init_mock(options) -- mock global variable 'KSR' local ksr_mock = { pv = { get = function(key) if key == "$rU" then if options.rU ~= nil then return options.rU else return "01234567" end end end, sets = function(k, v) end, }, hdr = { append = function(header) end, } } _G["KSR"] = mock(ksr_mock) local message_mock = { is_invite = function() return options.is_invite end } _G["message"] = mock(message_mock) end local headers = require "kamailio/headers" [..]
  • 46. Fix your tests require 'busted.runner'() local function init_mock(options) -- mock global variable 'KSR' local ksr_mock = { pv = { get = function(key) if key == "$rU" then if options.rU ~= nil then return options.rU else return "01234567" end end end, sets = function(k, v) end, }, hdr = { append = function(header) end, } } _G["KSR"] = mock(ksr_mock) local message_mock = { is_invite = function() return options.is_invite end } _G["message"] = mock(message_mock) end local headers = require "kamailio/headers" [..]
  • 47. Fix your tests require 'busted.runner'() local function init_mock(options) -- mock global variable 'KSR' local ksr_mock = { pv = { get = function(key) if key == "$rU" then if options.rU ~= nil then return options.rU else return "01234567" end end end, sets = function(k, v) end, }, hdr = { append = function(header) end, } } _G["KSR"] = mock(ksr_mock) local message_mock = { is_invite = function() return options.is_invite end } _G["message"] = mock(message_mock) end local headers = require "kamailio/headers" [..]
  • 48. Fix your tests require 'busted.runner'() local function init_mock(options) [..] end describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() -- Initialize the mock init_mock{ rU = "*31021112345", is_invite = true } -- Call the function headers.suppress_cid_if_needed() -- Now check if everything is as expected assert.spy(KSR.pv.sets).was.called_with("$rU", "021112345") assert.spy(KSR.hdr.append).was.called_with("Privacy: id") end) end)
  • 49. Fix your tests require 'busted.runner'() local function init_mock(options) [..] end describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() -- Initialize the mock init_mock{ rU = "*31021112345", is_invite = true } -- Call the function headers.suppress_cid_if_needed() -- Now check if everything is as expected assert.spy(KSR.pv.sets).was.called_with("$rU", "021112345") assert.spy(KSR.hdr.append).was.called_with("Privacy: id") end) end)
  • 50. Fix your tests require 'busted.runner'() local function init_mock(options) [..] end describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() -- Initialize the mock init_mock{ rU = "*31021112345", is_invite = true } -- Call the function headers.suppress_cid_if_needed() -- Now check if everything is as expected assert.spy(KSR.pv.sets).was.called_with("$rU", "021112345") assert.spy(KSR.hdr.append).was.called_with("Privacy: id") end) end)
  • 51. Add more tests require 'busted.runner'() local function init_mock(options) [..] end describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() .. end) it("Caller dials *31*021112345", function() .. end) it("Caller dials *31#021112345", function() .. end) it("Caller dials *31%23021112345", function() .. end) it("Caller dials +4921112345", function() .. end) it("Caller dials *31+4921112345, non-INVITE", function() .. end) end)
  • 52. Add more tests require 'busted.runner'() local function init_mock(options) [..] end describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() .. end) it("Caller dials *31*021112345", function() .. end) it("Caller dials *31#021112345", function() .. end) it("Caller dials *31%23021112345", function() .. end) it("Caller dials +4921112345", function() init_mock{ rU = "+4921112345", is_invite = true } headers.suppress_cid_if_needed() assert.spy(KSR.pv.sets).was.called(0) assert.spy(KSR.hdr.append).was.called(0) end) it("Caller dials *31+4921112345, non-INVITE", function() init_mock{ rU = "*31+4921112345" } headers.suppress_cid_if_needed() assert.spy(KSR.pv.sets).was.called_with("$rU", "+4921112345") assert.spy(KSR.hdr.append).was.called(0) end) end)
  • 53. Add more tests require 'busted.runner'() local function init_mock(options) [..] end describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() .. end) it("Caller dials *31*021112345", function() .. end) it("Caller dials *31#021112345", function() .. end) it("Caller dials *31%23021112345", function() .. end) it("Caller dials +4921112345", function() init_mock{ rU = "+4921112345", is_invite = true } headers.suppress_cid_if_needed() assert.spy(KSR.pv.sets).was.called(0) assert.spy(KSR.hdr.append).was.called(0) end) it("Caller dials *31+4921112345, non-INVITE", function() init_mock{ rU = "*31+4921112345" } headers.suppress_cid_if_needed() assert.spy(KSR.pv.sets).was.called_with("$rU", "+4921112345") assert.spy(KSR.hdr.append).was.called(0) end) end)
  • 54. Add more tests require 'busted.runner'() local function init_mock(options) [..] end describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() .. end) it("Caller dials *31*021112345", function() .. end) it("Caller dials *31#021112345", function() .. end) it("Caller dials *31%23021112345", function() .. end) it("Caller dials +4921112345", function() init_mock{ rU = "+4921112345", is_invite = true } headers.suppress_cid_if_needed() assert.spy(KSR.pv.sets).was.called(0) assert.spy(KSR.hdr.append).was.called(0) end) it("Caller dials *31+4921112345, non-INVITE", function() init_mock{ rU = "*31+4921112345" } headers.suppress_cid_if_needed() assert.spy(KSR.pv.sets).was.called_with("$rU", "+4921112345") assert.spy(KSR.hdr.append).was.called(0) end) end)
  • 55. Add more tests require 'busted.runner'() local function init_mock(options) [..] end describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() .. end) it("Caller dials *31*021112345", function() .. end) it("Caller dials *31#021112345", function() .. end) it("Caller dials *31%23021112345", function() .. end) it("Caller dials +4921112345", function() init_mock{ rU = "+4921112345", is_invite = true } headers.suppress_cid_if_needed() assert.spy(KSR.pv.sets).was.called(0) assert.spy(KSR.hdr.append).was.called(0) end) it("Caller dials *31+4921112345, non-INVITE", function() init_mock{ rU = "*31+4921112345" } headers.suppress_cid_if_needed() assert.spy(KSR.pv.sets).was.called_with("$rU", "+4921112345") assert.spy(KSR.hdr.append).was.called(0) end) end)
  • 56. Fix your code kamailio/headers.lua rex = require "rex_pcre" message = require "kamailio.message" local headers = {} function headers.get_request_user() return KSR.pv.get("$rU") end function headers.set_request_user(value) KSR.pv.sets("$rU", value) end function headers.set_privacy_header() KSR.hdr.append("Privacy: id") end function headers.suppress_cid_if_needed() request_user = headers.get_request_user() if rex.find(request_user, "^*31([#]|%23)?") then headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", "")) if message.is_invite() then headers.set_privacy_header() end end end return headers
  • 57. Fix your code kamailio/headers.lua rex = require "rex_pcre" message = require "kamailio.message" local headers = {} function headers.get_request_user() return KSR.pv.get("$rU") end function headers.set_request_user(value) KSR.pv.sets("$rU", value) end function headers.set_privacy_header() KSR.hdr.append("Privacy: id") end function headers.suppress_cid_if_needed() request_user = headers.get_request_user() if rex.find(request_user, "^*31([#]|%23)?") then headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", "")) if message.is_invite() then headers.set_privacy_header() end end end return headers
  • 58. Fix your code kamailio/headers.lua rex = require "rex_pcre" message = require "kamailio.message" local headers = {} function headers.get_request_user() return KSR.pv.get("$rU") end function headers.set_request_user(value) KSR.pv.sets("$rU", value) end function headers.set_privacy_header() KSR.hdr.append("Privacy: id") end function headers.suppress_cid_if_needed() request_user = headers.get_request_user() if rex.find(request_user, "^*31([*#]|%23)?") then headers.set_request_user(rex.gsub(request_user, "^*31([*#]|%23)?", "")) if message.is_invite() then headers.set_privacy_header() end end end return headers
  • 59. Fix your code kamailio/headers.lua rex = require "rex_pcre" message = require "kamailio.message" local headers = {} function headers.get_request_user() return KSR.pv.get("$rU") end function headers.set_request_user(value) KSR.pv.sets("$rU", value) end function headers.set_privacy_header() KSR.hdr.append("Privacy: id") end function headers.suppress_cid_if_needed() request_user = headers.get_request_user() if rex.find(request_user, "^*31([*#]|%23)?") then headers.set_request_user(rex.gsub(request_user, "^*31([*#]|%23)?", "")) if message.is_invite() then headers.set_privacy_header() end end end return headers
  • 60. Rerun your tests require 'busted.runner'() local function init_mock(options) [..] end describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() .. end) it("Caller dials *31*021112345", function() .. end) it("Caller dials *31#021112345", function() .. end) it("Caller dials *31%23021112345", function() .. end) it("Caller dials +4921112345", function() .. end) it("Caller dials *31+4921112345, non-INVITE", function() .. end) end)
  • 61. Rerun your tests require 'busted.runner'() local function init_mock(options) [..] end describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() .. end) it("Caller dials *31*021112345", function() .. end) it("Caller dials *31#021112345", function() .. end) it("Caller dials *31%23021112345", function() .. end) it("Caller dials +4921112345", function() .. end) it("Caller dials *31+4921112345, non-INVITE", function() .. end) end)
  • 62. Too dry? Try it yourself: https://github.com/sipgate/lua-kamailio
  • 64. Want to chat? Stop by!