[Asterisk-code-review] SIP: Rewrite tcpauthtimeout test in python (testsuite[master])
Mark Michelson
asteriskteam at digium.com
Tue Jul 28 15:33:00 CDT 2015
Mark Michelson has uploaded a new change for review.
https://gerrit.asterisk.org/979
Change subject: SIP: Rewrite tcpauthtimeout test in python
......................................................................
SIP: Rewrite tcpauthtimeout test in python
What was formerly a single Lua test has been split into two python
tests, though the functionality of the tests is identical.
timeout_should_happen: In this test, arbitrary data is written to the
SIP TCP socket in Asterisk, and we ensure that the connection is dropped
after the configured time in sip.conf
timeout_should_not_happen: This is a simple invocation of a SIPp UAC
scenario. The call lasts longer than the configured auth timeout, but
since the call is successfully established, the timeout should be
irrelevant. The test passes if the SIPp scenario passes.
ASTERISK-25225
Reported by Matt Jordan
Change-Id: I4413f70bfb06110e8f1ba61041e0769214f2a539
---
D tests/channels/SIP/tcpauthtimeout/run-test
D tests/channels/SIP/tcpauthtimeout/test.lua
A tests/channels/SIP/tcpauthtimeout/tests.yaml
A tests/channels/SIP/tcpauthtimeout/timeout_should_happen/configs/ast1/sip.conf
A tests/channels/SIP/tcpauthtimeout/timeout_should_happen/test-config.yaml
A tests/channels/SIP/tcpauthtimeout/timeout_should_happen/timeout.py
A tests/channels/SIP/tcpauthtimeout/timeout_should_not_happen/configs/ast1/extensions.conf
A tests/channels/SIP/tcpauthtimeout/timeout_should_not_happen/configs/ast1/sip.conf
A tests/channels/SIP/tcpauthtimeout/timeout_should_not_happen/sipp/uac.xml
A tests/channels/SIP/tcpauthtimeout/timeout_should_not_happen/test-config.yaml
M tests/channels/SIP/tests.yaml
11 files changed, 304 insertions(+), 134 deletions(-)
git pull ssh://gerrit.asterisk.org:29418/testsuite refs/changes/79/979/2
diff --git a/tests/channels/SIP/tcpauthtimeout/run-test b/tests/channels/SIP/tcpauthtimeout/run-test
deleted file mode 100755
index 9a12d98..0000000
--- a/tests/channels/SIP/tcpauthtimeout/run-test
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/env bash
-set -e
-. lib/sh/lua.sh
-asttest -a /$AST_TEST_ROOT -s `dirname $0` $@
diff --git a/tests/channels/SIP/tcpauthtimeout/test.lua b/tests/channels/SIP/tcpauthtimeout/test.lua
deleted file mode 100644
index d20e93c..0000000
--- a/tests/channels/SIP/tcpauthtimeout/test.lua
+++ /dev/null
@@ -1,129 +0,0 @@
-have_error = false
-function print_error(err)
- print(err)
- have_error = true
-end
-
-function sipp_exec(to, from)
- return proc.exec_io("sipp",
- to,
- "-m", "1",
- "-t", "t1",
- "-sn", "uac",
- "-d", timeout * 2, -- keep calls up longer than the timeout
- "-i", from,
- "-p", 5060,
- "-timeout", "60",
- "-timeout_error"
- )
-end
-
-function sipp_check_error(p, index)
- local res, err = p:wait()
-
- if not res then
- print_error(err)
- return res, err
- end
- if res ~= 0 then
- print_error("error while connecting client " .. index .. " (sipp exited with status " .. res .. ")\n" .. p.stderr:read("*a"))
- end
-
- return res, err
-end
-
-function connect(addr)
- local sock, err = socket.tcp()
- if not sock then
- return nil, err
- end
-
- local res, err = sock:connect(addr, 5060)
- if not res then
- sock:close()
- return nil, err
- end
-
- return check_sock(sock)
-end
-
-function check_sock(sock)
- -- select then read from the sock to see if it is still up
- local read, _, err = socket.select({sock}, nil, 0.1)
- if read[1] ~= sock and err ~= "timeout" then
- return nil, err
- end
-
- -- if we have data, then there is probably a problem because chan_sip
- -- doesn't send anything to new sockets
- if read[1] == sock then
- local res, err = sock:receive(1);
- if not res then
- return nil, err
- end
- end
-
- return sock
-end
-
-function write_sock(sock, data)
- local res, err, i = nil, nil, 0
-
- while i < #data do
- res, err, i = sock:send(data, i)
- if err then
- return nil, err
- else
- i = res
- end
- end
- return true
-end
-
--- timeout connections after 5 seconds
-timeout = 5
-
-print("starting asterisk")
-a = ast.new()
-a["sip.conf"]["general"]["tcpauthtimeout"] = timeout
-a["sip.conf"]["general"]["tcpenable"] = "yes"
-sip_addr = "127.0.0." .. a.index
-sip_port = "5060"
-a["sip.conf"]["general"]["tcpbindaddr"] = sip_addr .. ":" .. sip_port
-
-a["extensions.conf"]["default"]["exten"] = "service,1,Answer"
-a["extensions.conf"]["default"]["exten"] = "service,n,Wait(60)"
-a:spawn()
-
-print("testing timeout of an unauthenticated session")
-sock = check("error connecting to asterisk via TCP", connect(sip_addr))
-posix.sleep(timeout + 1);
-fail_if(check_sock(sock), "asterisk did not close the connection after " .. timeout .. " seconds")
-
-print("testing timeout of an unauthenticated session after writing some data")
-sock = check("error connecting to asterisk via TCP", connect(sip_addr))
-check("error writing data to the socket", write_sock(sock, "hi, this is your tester standby"))
-posix.sleep(timeout + 1);
-fail_if(check_sock(sock), "asterisk did not close the connection after " .. timeout .. " seconds")
-
-print("testing timeout of an unauthenticated session after writing some different data")
-sock = check("error connecting to asterisk via TCP", connect(sip_addr))
-check("error writing data to the socket", write_sock(sock, "INVITE sip:service at 127.0.0.1:5060 SIP/2.0\r\n"))
-posix.sleep(timeout + 1);
-fail_if(check_sock(sock), "asterisk did not close the connection after " .. timeout .. " seconds")
-
-print("testing timeout of an unauthenticated session after writing data in bursts")
-sock = check("error connecting to asterisk via TCP", connect(sip_addr))
-check("error writing data to the socket", write_sock(sock, "hi, this is your tester standby\r\n"))
-posix.sleep(1)
-check("error writing data to the socket", write_sock(sock, "hi, this is your tester, again... standby"))
-posix.sleep(1)
-check("error writing data to the socket", write_sock(sock, "guess who?! yup, your tester... standby\r\n"))
-posix.sleep(timeout);
-fail_if(check_sock(sock), "asterisk did not close the connection after " .. timeout .. " seconds")
-
-print("testing timeout of an authenticated session (should not timeout)")
-sipp = sipp_exec(sip_addr, "127.0.0." .. a.index + 1)
-sipp_check_error(sipp, 1)
-fail_if(have_error, "our authenticated client a problem")
-
diff --git a/tests/channels/SIP/tcpauthtimeout/tests.yaml b/tests/channels/SIP/tcpauthtimeout/tests.yaml
new file mode 100644
index 0000000..15faa79
--- /dev/null
+++ b/tests/channels/SIP/tcpauthtimeout/tests.yaml
@@ -0,0 +1,4 @@
+# Enter tests here in the order they should be considered for execution:
+tests:
+ - test: 'timeout_should_happen'
+ - test: 'timeout_should_not_happen'
diff --git a/tests/channels/SIP/tcpauthtimeout/timeout_should_happen/configs/ast1/sip.conf b/tests/channels/SIP/tcpauthtimeout/timeout_should_happen/configs/ast1/sip.conf
new file mode 100644
index 0000000..97f5283
--- /dev/null
+++ b/tests/channels/SIP/tcpauthtimeout/timeout_should_happen/configs/ast1/sip.conf
@@ -0,0 +1,4 @@
+[general]
+tcpbindaddr=127.0.0.1:5060
+tcpenable=yes
+tcpauthtimeout=5
diff --git a/tests/channels/SIP/tcpauthtimeout/timeout_should_happen/test-config.yaml b/tests/channels/SIP/tcpauthtimeout/timeout_should_happen/test-config.yaml
new file mode 100644
index 0000000..5db8edc
--- /dev/null
+++ b/tests/channels/SIP/tcpauthtimeout/timeout_should_happen/test-config.yaml
@@ -0,0 +1,35 @@
+testinfo:
+ summary: 'Ensure TCP sessions close when auth timeout is reached.'
+ description: |
+ "This test opens a series of TCP connections with Asterisk and ensures that
+ each is eventually closed by Asterisk once the auth timeout is reached. The
+ following test scenarios are attempted:
+
+ * Open a TCP connection and then do nothing.
+ * Open a TCP connection and write some arbitrary data.
+ * Open a TCP connection and write some SIP-like data.
+ * Open a TCP connection and write several bursts of data.
+
+ After each scenario is performed, we ensure that the TCP connection has been
+ closed."
+
+test-modules:
+ add-test-to-search-path: True
+ test-object:
+ config-section: test-config
+ typename: 'test_case.TestCaseModule'
+ modules:
+ -
+ typename: 'timeout.TCPTimeoutModule'
+
+test-config:
+ connect-ami: True
+
+properties:
+ minversion: '1.8.0.0'
+ dependencies:
+ - python: 'twisted'
+ - python: 'starpy'
+ - asterisk: 'chan_sip'
+ tags:
+ - SIP
diff --git a/tests/channels/SIP/tcpauthtimeout/timeout_should_happen/timeout.py b/tests/channels/SIP/tcpauthtimeout/timeout_should_happen/timeout.py
new file mode 100644
index 0000000..76e9c5d
--- /dev/null
+++ b/tests/channels/SIP/tcpauthtimeout/timeout_should_happen/timeout.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python
+"""
+Copyright (C) 2015, Digium, Inc.
+Mark Michelson <mmichelson at digium.com>
+
+This program is free software, distributed under the terms of
+the GNU General Public License Version 2.
+"""
+
+import logging
+import time
+
+from twisted.internet.protocol import Protocol, ClientFactory
+from twisted.internet import reactor
+
+LOGGER = logging.getLogger(__name__)
+
+'''The TCP client connections to create
+
+Each item in this list is a list of strings to write on the
+TCP connection once the connection is established. Even though
+we are connecting to a SIP socket, we write arbitrary non-SIP
+data in all cases.
+'''
+TCP_MESSAGES = [
+ [],
+ ['hi, this is your tester standby'],
+ ['INVITE sip:service at 127.0.0.1:5060 SIP/2.0\r\n'],
+ ['hi, this is your tester standby',
+ 'hi, this is your tester, again... standby',
+ 'guess who?! yup, your tester... standby\r\n']
+]
+
+
+class SIPPoker(Protocol):
+ def __init__(self, messages, factory):
+ self.messages = messages
+ self.factory = factory
+
+ def connectionMade(self):
+ '''twisted overload for when TCP connection is established
+
+ We log the time the connection was made and then write all
+ TCP messages for this particular connection
+ '''
+ self.factory.set_connection_time()
+ for message in self.messages:
+ self.transport.write(message)
+
+
+class SIPClientFactory(ClientFactory):
+ def __init__(self, module, messages):
+ self.module = module
+ self.messages = messages
+ self.connection_time = None
+
+ def buildProtocol(self, addr):
+ '''twisted overload used to create client'''
+ return SIPPoker(self.messages, self)
+
+ def clientConnectionLost(self, connector, reason):
+ '''twisted overload called when connection is lost
+
+ We expect this to be called for each TCP client connection
+ we have established. We also do our due diligence to ensure
+ that the connection actually timed out as expected, and the
+ connection was not lost for some other unexpected reason
+ '''
+ if not self.connection_time:
+ LOGGER.error("Client lost connection before connection was made")
+ self.module.failed_timeout()
+
+ timeout = time.time() - self.connection_time
+ if timeout < 4.5 or timeout > 5.5:
+ LOGGER.error("Client timeout {0} outside of bounds".format(timeout))
+ self.module.failed_timeout()
+
+ LOGGER.info("Client timed out as expected")
+ self.module.successful_timeout()
+
+ def clientConnectionFailed(self, connector, reason):
+ '''twiseted overload indicating the TCP connection failed'''
+ LOGGER.info("Client connection failed.")
+ self.module.failed_timeout()
+
+ def set_connection_time(self):
+ '''Denote when the TCP connection was established'''
+ self.connection_time = time.time()
+
+
+class TCPTimeoutModule(object):
+ '''Entry point for pluggable module
+
+ This sets up the TCP client connections using TCP_MESSAGES
+ to determine the number of clients to set up. This class
+ serves as the relay between the actual connections and the
+ testsuite test object.
+ '''
+ def __init__(self, module_config, test_object):
+ self.timeouts = 0
+ self.test_object = test_object
+ test_object.register_ami_observer(self.ami_connected)
+
+ def ami_connected(self, ami):
+ '''Set up TCP client connections
+
+ We do not actually require AMI, but we can ensure that Asterisk
+ is fully booted and ready to accept incoming TCP connections
+ once this callback is called
+ '''
+ for messages in TCP_MESSAGES:
+ reactor.connectTCP('127.0.0.1', 5060,
+ SIPClientFactory(self, messages))
+
+ def successful_timeout(self):
+ '''A TCP client has timed out as expected
+
+ Once all TCP client connections have timed out, the test
+ passes
+ '''
+ self.timeouts += 1
+ if self.timeouts == len(TCP_MESSAGES):
+ self.test_object.set_passed(True)
+ self.test_object.stop_reactor()
+
+ def failed_timeout(self):
+ '''A TCP client connection terminated incorrectly
+
+ This may be because the connection's timeout fell outside
+ of an acceptable interval, or it may be that the connection
+ could not be made in the first place
+ '''
+ self.test_object.set_passed(False)
+ self.test_object.stop_reactor()
diff --git a/tests/channels/SIP/tcpauthtimeout/timeout_should_not_happen/configs/ast1/extensions.conf b/tests/channels/SIP/tcpauthtimeout/timeout_should_not_happen/configs/ast1/extensions.conf
new file mode 100644
index 0000000..9f3a748
--- /dev/null
+++ b/tests/channels/SIP/tcpauthtimeout/timeout_should_not_happen/configs/ast1/extensions.conf
@@ -0,0 +1,5 @@
+[default]
+exten => echo,1,NoOp()
+exten => echo,n,Answer()
+exten => echo,n,Echo()
+exten => echo,n,Hangup()
diff --git a/tests/channels/SIP/tcpauthtimeout/timeout_should_not_happen/configs/ast1/sip.conf b/tests/channels/SIP/tcpauthtimeout/timeout_should_not_happen/configs/ast1/sip.conf
new file mode 100644
index 0000000..f8b7d93
--- /dev/null
+++ b/tests/channels/SIP/tcpauthtimeout/timeout_should_not_happen/configs/ast1/sip.conf
@@ -0,0 +1,10 @@
+[general]
+tcpenable = yes
+tcpbindaddr = 127.0.0.1:5060
+tcpauthtimeout = 3
+
+[sipp]
+type = peer
+context = default
+host = 127.0.0.1
+port = 5061
diff --git a/tests/channels/SIP/tcpauthtimeout/timeout_should_not_happen/sipp/uac.xml b/tests/channels/SIP/tcpauthtimeout/timeout_should_not_happen/sipp/uac.xml
new file mode 100644
index 0000000..721806b
--- /dev/null
+++ b/tests/channels/SIP/tcpauthtimeout/timeout_should_not_happen/sipp/uac.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+<scenario name="Basic Sipstone UAC">
+ <send retrans="500">
+ <![CDATA[
+
+ INVITE sip:[service]@[remote_ip]:[remote_port] SIP/2.0
+ Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+ From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
+ To: [service] <sip:[service]@[remote_ip]:[remote_port]>
+ Call-ID: [call_id]
+ CSeq: 1 INVITE
+ Contact: sip:sipp@[local_ip]:[local_port]
+ Max-Forwards: 70
+ Subject: Performance Test
+ Content-Type: application/sdp
+ Content-Length: [len]
+
+ v=0
+ o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
+ s=-
+ c=IN IP[media_ip_type] [media_ip]
+ t=0 0
+ m=audio [media_port] RTP/AVP 0
+ a=rtpmap:0 PCMU/8000
+
+ ]]>
+ </send>
+
+ <recv response="100"
+ optional="true">
+ </recv>
+
+ <recv response="180" optional="true">
+ </recv>
+
+ <recv response="183" optional="true">
+ </recv>
+
+ <recv response="200" rtd="true">
+ </recv>
+
+ <send>
+ <![CDATA[
+
+ ACK sip:[service]@[remote_ip]:[remote_port] SIP/2.0
+ Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+ From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
+ To: [service] <sip:[service]@[remote_ip]:[remote_port]>[peer_tag_param]
+ Call-ID: [call_id]
+ CSeq: 1 ACK
+ Contact: sip:sipp@[local_ip]:[local_port]
+ Max-Forwards: 70
+ Subject: Performance Test
+ Content-Length: 0
+
+ ]]>
+ </send>
+
+ <pause/>
+
+ <send retrans="500">
+ <![CDATA[
+
+ BYE sip:[service]@[remote_ip]:[remote_port] SIP/2.0
+ Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+ From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
+ To: [service] <sip:[service]@[remote_ip]:[remote_port]>[peer_tag_param]
+ Call-ID: [call_id]
+ CSeq: 2 BYE
+ Contact: sip:sipp@[local_ip]:[local_port]
+ Max-Forwards: 70
+ Subject: Performance Test
+ Content-Length: 0
+
+ ]]>
+ </send>
+
+ <recv response="200" crlf="true">
+ </recv>
+</scenario>
+
diff --git a/tests/channels/SIP/tcpauthtimeout/timeout_should_not_happen/test-config.yaml b/tests/channels/SIP/tcpauthtimeout/timeout_should_not_happen/test-config.yaml
new file mode 100644
index 0000000..5fbe8ef
--- /dev/null
+++ b/tests/channels/SIP/tcpauthtimeout/timeout_should_not_happen/test-config.yaml
@@ -0,0 +1,29 @@
+testinfo:
+ summary: 'Ensure vetted TCP session does not trigger the auth timeout'
+ description: |
+ "This test uses a SIPp scenario to establish a call with Asterisk.
+ The call is configured to last longer than the configured TCP auth
+ timeout in sip.conf. We ensure that the SIPp scenario runs to its
+ conclusion and is not stopped prematurely."
+
+test-modules:
+ test-object:
+ config-section: sipp-config
+ typename: 'sipp.SIPpTestCase'
+
+sipp-config:
+ fail-on-any: True
+ test-iterations:
+ -
+ scenarios:
+ - { 'key-args': {'scenario': 'uac.xml', '-s': 'echo', '-d': '7000', '-p': '5061', '-t': 't1'} }
+
+properties:
+ minvsersion: '1.8.0.0'
+ dependencies:
+ - python: 'twisted'
+ - python: 'starpy'
+ - asterisk: 'chan_sip'
+ tags:
+ - SIP
+
diff --git a/tests/channels/SIP/tests.yaml b/tests/channels/SIP/tests.yaml
index 9fcda79..f0c6ce6 100644
--- a/tests/channels/SIP/tests.yaml
+++ b/tests/channels/SIP/tests.yaml
@@ -6,7 +6,7 @@
- test: 'refer_replaces_to_self'
- test: 'info_dtmf'
- test: 'tcpauthlimit'
- - test: 'tcpauthtimeout'
+ - dir: 'tcpauthtimeout'
- test: 'sip_outbound_address'
- test: 'sip_attended_transfer'
- test: 'sip_attended_transfer_tcp'
--
To view, visit https://gerrit.asterisk.org/979
To unsubscribe, visit https://gerrit.asterisk.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I4413f70bfb06110e8f1ba61041e0769214f2a539
Gerrit-PatchSet: 2
Gerrit-Project: testsuite
Gerrit-Branch: master
Gerrit-Owner: Mark Michelson <mmichelson at digium.com>
More information about the asterisk-code-review
mailing list