diff --git a/debian/control b/debian/control index 1ea05ef..0b73b5f 100644 --- a/debian/control +++ b/debian/control @@ -1,8 +1,9 @@ Source: lua-ngcp-kamailio Priority: extra Maintainer: Sipwise Development Team -Build-Depends: debhelper (>= 8~), - dh-lua (>= 16~) +Build-Depends: + debhelper (>= 8~), + dh-lua (>= 16~), Standards-Version: 3.9.7 Section: libs Homepage: http://sipwise.com/ @@ -10,12 +11,14 @@ Homepage: http://sipwise.com/ Package: lua-ngcp-kamailio Section: libs Architecture: any -Depends: lua-redis, - lua-sql-mysql, - ngcp-system-tools-ce | ngcp-system-tools, - ${misc:Depends}, - ${shlibs:Depends} -Provides: ${lua:Provides} +Depends: + lua-sql-mysql, + lua5.1, + ngcp-system-tools-ce | ngcp-system-tools, + ${misc:Depends}, + ${shlibs:Depends}, +Provides: + ${lua:Provides}, XB-Lua-Versions: ${lua:Versions} Description: lua framework for ngcp-kamailio config This package provides the lua ngcp lib that ngcp-template-kamailio @@ -24,13 +27,15 @@ Description: lua framework for ngcp-kamailio config Package: lua-ngcp-kamailio-dev Section: libs Architecture: any -Depends: lua-lemock, - lua-logging, - lua-sql-mysql, - lua-unit, - ${misc:Depends}, - ${shlibs:Depends} -Provides: ${lua:Provides} +Depends: + lua-lemock, + lua-logging, + lua-sql-mysql, + lua-unit, + ${misc:Depends}, + ${shlibs:Depends}, +Provides: + ${lua:Provides}, XB-Lua-Versions: ${lua:Versions} Description: lua framework for ngcp-kamailio config development tests This package provides the lua ngcp lib that ngcp-template-kamailio diff --git a/debian/lua-ngcp-kamailio-dev.docs b/debian/lua-ngcp-kamailio-dev.docs index 8692213..2de21aa 100644 --- a/debian/lua-ngcp-kamailio-dev.docs +++ b/debian/lua-ngcp-kamailio-dev.docs @@ -1,4 +1,4 @@ -tests -tests_v mocks run_tests.sh +tests +tests_v diff --git a/debian/rules b/debian/rules index e167251..4d97c2e 100755 --- a/debian/rules +++ b/debian/rules @@ -11,3 +11,9 @@ %: dh $@ --buildsystem=lua --with lua + +override_dh_auto_install: + dh_auto_install + dh_install scripts/ngcp-dlgcnt-clean /usr/sbin + dh_install scripts/ngcp-dlglist-clean /usr/sbin + dh_install scripts/ngcp-dlgcnt-check /usr/sbin diff --git a/scripts/ngcp-dlgcnt-check b/scripts/ngcp-dlgcnt-check new file mode 100755 index 0000000..796e23a --- /dev/null +++ b/scripts/ngcp-dlgcnt-check @@ -0,0 +1,59 @@ +#!/bin/bash + +set -e + +REMOVE=false + +function usage() { + printf "%s [options]\n" "$(basename "$0")" + printf "\toptions\n" + printf "\t-h this help\n" + printf "\t-r remove unknown callids from redis properly\n" +} + +while getopts "rh" opt; do + case $opt in + h) usage; exit 0;; + r) REMOVE=true;; + \?) usage; exit 1;; + esac +done +shift $((OPTIND-1)) + +if [[ $# -ne 0 ]] ; then + usage + exit 2 +fi + +if ! ngcp-check_active -q ; then + echo "node is not active, abort" >&2 + exit 3 +fi + +# read kamailio.proxy.dlgcnt.pair_redis_db +REDIS_DB="$(ngcp-dlgcnt-clean -c|| echo 4)" + +# full list +REDIS_CALLIDS=$(mktemp) +ngcp-redis-helper -n "$REDIS_DB" dump | egrep -v "^$" > "$REDIS_CALLIDS" || true +# 'lists:' belongs to dlglist +REDIS_CALLIDS_FILTER=$(mktemp) +egrep -v '^list:' "$REDIS_CALLIDS" > "$REDIS_CALLIDS_FILTER" || true + +# list of dialogs known by kamailio +KAM_CALLIDS=$(mktemp) +ngcp-sercmd proxy dlg.list | awk -F: '/call-id:/ { print $2}' > "$KAM_CALLIDS" + +while read -r i ; do + if ! grep -q "$i" "$KAM_CALLIDS" ; then + printf "CallID:[%s] unknown\n" "$i" + if $REMOVE ; then + ngcp-dlgcnt-clean "$i" || true + if grep -q "list:$i" "$REDIS_CALLIDS" ; then + ngcp-dlglist-clean "$i" || true + fi + fi + fi +done < "$REDIS_CALLIDS_FILTER" + +rm -f "$KAM_CALLIDS" "$REDIS_CALLIDS" "$REDIS_CALLIDS_FILTER" diff --git a/scripts/ngcp-dlgcnt-clean b/scripts/ngcp-dlgcnt-clean new file mode 100755 index 0000000..9631ea6 --- /dev/null +++ b/scripts/ngcp-dlgcnt-clean @@ -0,0 +1,51 @@ +#!/usr/bin/env lua5.1 +--local ut = require 'ngcp.utils'.table +local NGCPDlg = require 'ngcp.dlgcnt' +local config = "/etc/kamailio/proxy/dlgcnt.lua.cfg" + +-- luacheck: globals dlg_config + +local function get_config() + if os.getenv('DLG_CONFIG') then + config = os.getenv('DLG_CONFIG') + end + + local ok,e = pcall(dofile,config) + if not ok then + io.stderr:write(e..'\n') + print("using defaults\n") + end +end + +local function usage() + print("ngcp-dlgcnt-clean callid") +end + +if #arg ~= 1 then + io.stderr:write("wrong number of arguments\n"); + usage() + os.exit(2) +end + +if arg[1] == "-h" then usage(); os.exit(0); end + +get_config() + +local dlg = NGCPDlg:new() + +if arg[1] == '-c' then + print(tostring(dlg.config.pair.db)) + os.exit(0) +end + +if dlg_config then + dlg.config.central.host = dlg_config.central.host + dlg.config.central.port = dlg_config.central.port + dlg.config.central.db = dlg_config.central.db + dlg.config.pair.host = dlg_config.pair.host + dlg.config.pair.port = dlg_config.pair.port + dlg.config.pair.db = dlg_config.pair.db + --print(string.format("dlg.config:%s", ut.tostring(dlg.config))) +end + +dlg:del(arg[1]) diff --git a/scripts/ngcp-dlglist-clean b/scripts/ngcp-dlglist-clean new file mode 100644 index 0000000..3bfaab7 --- /dev/null +++ b/scripts/ngcp-dlglist-clean @@ -0,0 +1,45 @@ +#!/usr/bin/env lua5.1 +--local ut = require 'ngcp.utils'.table +local NGCPDlgList = require 'ngcp.dlglist' +local config = "/etc/kamailio/proxy/dlgcnt.lua.cfg" + +-- luacheck: globals dlg_config + +local function get_config() + if os.getenv('DLG_CONFIG') then + config = os.getenv('DLG_CONFIG') + end + + local ok,e = pcall(dofile,config) + if not ok then + io.stderr:write(e..'\n') + print("using defaults\n") + end +end + +local function usage() + print("ngcp-dlglist-clean callid") +end + +if #arg ~= 1 then + io.stderr:write("wrong number of arguments\n"); + usage() + os.exit(2) +end + +if arg[1] == "-h" then usage(); os.exit(0); end + +get_config() + +local dlg = NGCPDlgList.new() +if dlg_config then + dlg.config.central.host = dlg_config.central.host + dlg.config.central.port = dlg_config.central.port + dlg.config.central.db = dlg_config.central.db + dlg.config.pair.host = dlg_config.pair.host + dlg.config.pair.port = dlg_config.pair.port + dlg.config.pair.db = dlg_config.pair.db + --print(string.format("dlg.config:%s", ut.tostring(dlg.config))) +end + +dlg:destroy(arg[1]) diff --git a/t/Dockerfile b/t/Dockerfile index 1fe0aae..91285ef 100644 --- a/t/Dockerfile +++ b/t/Dockerfile @@ -5,12 +5,12 @@ FROM docker.mgm.sipwise.com/sipwise-jessie:latest # is updated with the current date. It will force refresh of all # of the base images and things like `apt-get update` won't be using # old cached versions when the Dockerfile is built. -ENV REFRESHED_AT 2016-01-12 +ENV REFRESHED_AT 2016-11-28 # TODO - the release-trunk-jessie is too dynamic yet, though required for lua-unit backport RUN echo "deb https://deb.sipwise.com/autobuild/ release-trunk-jessie main" >>/etc/apt/sources.list -RUN apt-get update && apt-get install --assume-yes lua5.1 lua-unit lua-lemock lua-logging +RUN apt-get update && apt-get install --assume-yes lua5.1 lua-unit lua-lemock lua-logging python-xmlrunner RUN echo './t/testrunner' >>/root/.bash_history diff --git a/t/testrunner b/t/testrunner index 379bc29..38623ab 100755 --- a/t/testrunner +++ b/t/testrunner @@ -18,3 +18,4 @@ else fi RESULTS=${RESULTS} FORMAT=JUNIT ./run_tests.sh +WORKSPACE=${RESULTS}/tmp ./tests/test_dlgcnt.py > ${RESULTS}/reports/test_dlgcnt.xml diff --git a/tests/fixtures/dlgcnt.lua.cfg b/tests/fixtures/dlgcnt.lua.cfg new file mode 100644 index 0000000..3ea983f --- /dev/null +++ b/tests/fixtures/dlgcnt.lua.cfg @@ -0,0 +1,12 @@ +dlg_config = { + central = { + host = '127.2.0.1', + port = 6379, + db = 3 + }, + pair = { + host = '127.2.0.1', + port = 6379, + db = 4 + }, +} diff --git a/tests/fixtures/koredis.cmd b/tests/fixtures/koredis.cmd new file mode 100644 index 0000000..e14f3bb --- /dev/null +++ b/tests/fixtures/koredis.cmd @@ -0,0 +1,3 @@ +ngcp-dlgcnt-clean f81f8a33-2dbd-1235-018c-0090fb484030-pbx_1 +ngcp-dlgcnt-clean f81f8a33-2dbd-1235-018c-0090fb484030 +ngcp-dlglist-clean f81f8a33-2dbd-1235-018c-0090fb484030 diff --git a/tests/fixtures/koredis.dlg b/tests/fixtures/koredis.dlg new file mode 100644 index 0000000..dcecfdf --- /dev/null +++ b/tests/fixtures/koredis.dlg @@ -0,0 +1,51 @@ +{ + h_entry: 51 + h_id: 3315 + call-id: WSGH2SB52VHYLPX4VQQ6N4B2ZM@81.201.82.107 + from_uri: sip:390649904073@voxbone.com + to_uri: sip:390240708163@46.29.177.34 + state: 4 + start_ts: 1480083143 + init_ts: 1480083140 + timeout: 1480126343 + lifetime: 43200 + dflags: 512 + sflags: 0 + iflags: 0 + caller: { + tag: as2da8b920 + contact: sip:390649904073@81.201.82.107:5060;transport=udp + cseq: 102 + route_set: ,, + socket: udp:203.0.113.113:5062 + } + callee: { + tag: 2B56584B-583846C4000C078A-A5F9B700 + contact: sip:390240708163@203.0.113.113:5080 + cseq: 0 + route_set: + socket: udp:203.0.113.113:5062 + } + profiles: { + } + variables: { + { + recent_calls_user_online: 1 + } + { + recent_calls_caller: 390649904073 + } + { + start_time: 1480083143 + } + { + callee_uuid: 38de282f-5c5c-4d71-8a0d-b300a68d86ec + } + { + recent_calls_key: 38de282f-5c5c-4d71-8a0d-b300a68d86ec + } + { + lua_dlg_callid: WSGH2SB52VHYLPX4VQQ6N4B2ZM@81.201.82.107 + } + } +} diff --git a/tests/fixtures/koredis.redis b/tests/fixtures/koredis.redis new file mode 100644 index 0000000..4868f23 --- /dev/null +++ b/tests/fixtures/koredis.redis @@ -0,0 +1,4 @@ +WSGH2SB52VHYLPX4VQQ6N4B2ZM@81.201.82.107 +f81f8a33-2dbd-1235-018c-0090fb484030-pbx_1 +f81f8a33-2dbd-1235-018c-0090fb484030 +list:f81f8a33-2dbd-1235-018c-0090fb484030 diff --git a/tests/fixtures/okredis.dlg b/tests/fixtures/okredis.dlg new file mode 100644 index 0000000..8173776 --- /dev/null +++ b/tests/fixtures/okredis.dlg @@ -0,0 +1,102 @@ +{ + h_entry: 51 + h_id: 3315 + call-id: WSGH2SB52VHYLPX4VQQ6N4B2ZM@81.201.82.107 + from_uri: sip:390649904073@voxbone.com + to_uri: sip:390240708163@46.29.177.34 + state: 4 + start_ts: 1480083143 + init_ts: 1480083140 + timeout: 1480126343 + lifetime: 43200 + dflags: 512 + sflags: 0 + iflags: 0 + caller: { + tag: as2da8b920 + contact: sip:390649904073@81.201.82.107:5060;transport=udp + cseq: 102 + route_set: ,, + socket: udp:203.0.113.113:5062 + } + callee: { + tag: 2B56584B-583846C4000C078A-A5F9B700 + contact: sip:390240708163@203.0.113.113:5080 + cseq: 0 + route_set: + socket: udp:203.0.113.113:5062 + } + profiles: { + } + variables: { + { + recent_calls_user_online: 1 + } + { + recent_calls_caller: 390649904073 + } + { + start_time: 1480083143 + } + { + callee_uuid: 38de282f-5c5c-4d71-8a0d-b300a68d86ec + } + { + recent_calls_key: 38de282f-5c5c-4d71-8a0d-b300a68d86ec + } + { + lua_dlg_callid: WSGH2SB52VHYLPX4VQQ6N4B2ZM@81.201.82.107 + } + } +} +{ + h_entry: 52 + h_id: 3316 + call-id: WSGH2SB52VHYLPX4VQQ6N4B2ZM@81.201.82.107-pbx_1 + from_uri: sip:390649904073@voxbone.com + to_uri: sip:390240708163@46.29.177.34 + state: 4 + start_ts: 1480083143 + init_ts: 1480083140 + timeout: 1480126343 + lifetime: 43200 + dflags: 512 + sflags: 0 + iflags: 0 + caller: { + tag: as2da8b920 + contact: sip:390649904073@81.201.82.107:5060;transport=udp + cseq: 102 + route_set: ,, + socket: udp:203.0.113.113:5062 + } + callee: { + tag: 2B56584B-583846C4000C078A-A5F9B700 + contact: sip:390240708163@203.0.113.113:5080 + cseq: 0 + route_set: + socket: udp:203.0.113.113:5062 + } + profiles: { + } + variables: { + { + recent_calls_user_online: 1 + } + { + recent_calls_caller: 390649904073 + } + { + start_time: 1480083143 + } + { + callee_uuid: 38de282f-5c5c-4d71-8a0d-b300a68d86ec + } + { + recent_calls_key: 38de282f-5c5c-4d71-8a0d-b300a68d86ec + } + { + lua_dlg_callid: WSGH2SB52VHYLPX4VQQ6N4B2ZM@81.201.82.107-pbx_1 + } + } +} diff --git a/tests/fixtures/okredis.redis b/tests/fixtures/okredis.redis new file mode 100644 index 0000000..b40b325 --- /dev/null +++ b/tests/fixtures/okredis.redis @@ -0,0 +1,3 @@ +WSGH2SB52VHYLPX4VQQ6N4B2ZM@81.201.82.107-pbx_1 +WSGH2SB52VHYLPX4VQQ6N4B2ZM@81.201.82.107 +list:WSGH2SB52VHYLPX4VQQ6N4B2ZM@81.201.82.107 diff --git a/tests/test_dlgcnt.py b/tests/test_dlgcnt.py new file mode 100755 index 0000000..158c953 --- /dev/null +++ b/tests/test_dlgcnt.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python +from __future__ import print_function +import subprocess +import sys +import unittest +import xmlrunner +import os +import io +import shutil +import copy + +WORKSPACE = os.path.abspath(os.getenv('WORKSPACE', '/tmp')) +FIXTURES = os.path.join(os.getcwd(), "tests/fixtures") +FAKE_CMD_LIST = os.path.join(WORKSPACE, "cmd_list") +FAKE_BIN = os.path.join(WORKSPACE, "bin") +FAKE_PATH = "%s:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin" % FAKE_BIN + + +def executeAndReturnOutput(command): + p = subprocess.Popen(command, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdoutdata, stderrdata = p.communicate() + #print(stdoutdata, file=sys.stdout) + print(stderrdata, file=sys.stderr) + return p.returncode, stdoutdata, stderrdata + + +def create_prog(filename, command): + """Create test program. + + :param unicode filename: destination filename + :param unicode command: command to write to test program + """ + with io.open(filename, 'w', encoding='utf-8') as fp: + fp.write(u"#!/bin/bash\n%s\n" % (command, )) + os.fchmod(fp.fileno(), 0o755) + + +def setnode(active=True): + if active: + mode = 'true' + else: + mode = 'false' + create_prog(os.path.join(FAKE_BIN, 'ngcp-check_active'), mode) + +command = [ + "env", + "PATH=%s" % FAKE_PATH, + "./scripts/ngcp-dlgcnt-check", "-r"] + +FAKE_DLG_CLEAN = """ +if [ $1 = -c ] ; then + echo 4 + exit 0 +fi +echo "ngcp-dlgcnt-clean $*">> %s""" % FAKE_CMD_LIST + +FAKE_REDIS_HELPER = """ +if [ $1 != -n ] && [ $2 != 4 ] ; then + echo $0 $* >&2 + exit 1 +fi""" + + +class TestDlgCnt(unittest.TestCase): + + def checkNotCmd(self): + self.assertFalse(os.path.exists(FAKE_CMD_LIST), + "%s found" % FAKE_CMD_LIST) + + def checkCmd(self, f, f2): + self.assertTrue(os.path.exists(f), "%s not found" % f) + self.assertTrue(os.path.exists(f2), "%s not found" % f2) + res = executeAndReturnOutput( + ['diff', '-uNb', f, f2]) + self.assertEqual(res[0], 0, res[1]) + + def setUp(self): + self.command = copy.deepcopy(command) + if not os.path.exists(FAKE_BIN): + os.makedirs(FAKE_BIN) + setnode(True) + create_prog(os.path.join(FAKE_BIN, 'ngcp-dlgcnt-clean'), + FAKE_DLG_CLEAN) + create_prog(os.path.join(FAKE_BIN, 'ngcp-dlglist-clean'), + 'echo "ngcp-dlglist-clean $*">> %s' % FAKE_CMD_LIST) + + def tearDown(self): + shutil.rmtree(WORKSPACE) + + def test_wrong_option(self): + self.command = ["./scripts/ngcp-dlgcnt-check", "-k"] + res = executeAndReturnOutput(self.command) + self.assertEqual(res[0], 1, res[2]) + self.assertRegexpMatches(res[2], 'illegal option') + self.checkNotCmd() + + def test_help(self): + self.command = ["./scripts/ngcp-dlgcnt-check", "-h"] + res = executeAndReturnOutput(self.command) + self.assertEqual(res[0], 0, res[2]) + self.assertRegexpMatches(res[1], '\toptions\n') + self.checkNotCmd() + + def test_inactive(self): + setnode(False) + res = executeAndReturnOutput(self.command) + self.assertEqual(res[0], 3, res[1]) + self.checkNotCmd() + + def test_redisconf(self): + create_prog(os.path.join(FAKE_BIN, 'ngcp-sercmd'), + "true") + create_prog(os.path.join(FAKE_BIN, 'ngcp-redis-helper'), + "%s; true" % FAKE_REDIS_HELPER) + res = executeAndReturnOutput(self.command) + self.assertEqual(res[0], 0, res[1]) + self.checkNotCmd() + + def test_empty(self): + create_prog(os.path.join(FAKE_BIN, 'ngcp-sercmd'), + "true") + create_prog(os.path.join(FAKE_BIN, 'ngcp-redis-helper'), + "true") + res = executeAndReturnOutput(self.command) + self.assertEqual(res[0], 0, res[1]) + self.checkNotCmd() + + def test_empty_line(self): + create_prog(os.path.join(FAKE_BIN, 'ngcp-sercmd'), + "echo") + create_prog(os.path.join(FAKE_BIN, 'ngcp-redis-helper'), + "echo") + res = executeAndReturnOutput(self.command) + self.assertEqual(res[0], 0, res[1]) + self.checkNotCmd() + + def test_okredis(self): + FAKE_DLG = os.path.join(FIXTURES, 'okredis.dlg') + create_prog(os.path.join(FAKE_BIN, 'ngcp-sercmd'), + "cat %s" % (FAKE_DLG)) + FAKE_REDIS = os.path.join(FIXTURES, 'okredis.redis') + create_prog(os.path.join(FAKE_BIN, 'ngcp-redis-helper'), + "cat %s" % (FAKE_REDIS)) + res = executeAndReturnOutput(self.command) + self.assertEqual(res[0], 0, res[2]) + self.checkNotCmd() + + def test_koredis(self): + FAKE_DLG = os.path.join(FIXTURES, 'koredis.dlg') + create_prog(os.path.join(FAKE_BIN, 'ngcp-sercmd'), + "cat %s" % (FAKE_DLG)) + FAKE_REDIS = os.path.join(FIXTURES, 'koredis.redis') + create_prog(os.path.join(FAKE_BIN, 'ngcp-redis-helper'), + "cat %s" % (FAKE_REDIS)) + res = executeAndReturnOutput(self.command) + self.assertEqual(res[0], 0, res[2]) + self.checkCmd(os.path.join(FIXTURES, 'koredis.cmd'), + FAKE_CMD_LIST) + + def test_kodlgclean(self): + FAKE_DLG = os.path.join(FIXTURES, 'koredis.dlg') + create_prog(os.path.join(FAKE_BIN, 'ngcp-sercmd'), + "cat %s" % (FAKE_DLG)) + FAKE_REDIS = os.path.join(FIXTURES, 'koredis.redis') + create_prog(os.path.join(FAKE_BIN, 'ngcp-redis-helper'), + "cat %s" % (FAKE_REDIS)) + create_prog(os.path.join(FAKE_BIN, 'ngcp-dlgcnt-clean'), + '%s; false' % FAKE_DLG_CLEAN) + res = executeAndReturnOutput(self.command) + self.assertEqual(res[0], 0, res[2]) + self.checkCmd(os.path.join(FIXTURES, 'koredis.cmd'), + FAKE_CMD_LIST) + + def test_kolistclean(self): + FAKE_DLG = os.path.join(FIXTURES, 'koredis.dlg') + create_prog(os.path.join(FAKE_BIN, 'ngcp-sercmd'), + "cat %s" % (FAKE_DLG)) + FAKE_REDIS = os.path.join(FIXTURES, 'koredis.redis') + create_prog(os.path.join(FAKE_BIN, 'ngcp-redis-helper'), + "cat %s" % (FAKE_REDIS)) + create_prog(os.path.join(FAKE_BIN, 'ngcp-dlglist-clean'), + 'echo "ngcp-dlglist-clean $*">> %s; false' % FAKE_CMD_LIST) + res = executeAndReturnOutput(self.command) + self.assertEqual(res[0], 0, res[2]) + self.checkCmd(os.path.join(FIXTURES, 'koredis.cmd'), + FAKE_CMD_LIST) + +if __name__ == '__main__': + unittest.main( + testRunner=xmlrunner.XMLTestRunner(output=sys.stdout), + # these make sure that some options that are not applicable + # remain hidden from the help menu. + failfast=False, buffer=False, catchbreak=False)