yate/share/scripts/yaypm/yaypm/utils/tester/dtmfetc.py

243 lines
8.3 KiB
Python

#!/usr/bin/python
# -*- coding: iso-8859-2; -*-
"""
Tester module for YAYPM. Generates calls, and allows to define incall
activity like sending DTMFs. Can be run on two separate yates to imitate
realistic load.
Copyright (C) 2005 Maciek Kaminski
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
"""
from yaypm import TCPDispatcherFactory, AbandonedException
from yaypm.flow import go, logger_flow, getResult
from yaypm.utils import XOR, OR, sleep #, tester
from twisted.internet import reactor, defer
import sys, logging, yaypm, time
logger = logging.getLogger("tester")
class peep:
def __init__(self, g):
self.g = g
self.peep = None
def __iter__(self):
return self
def next(self):
if self.peep:
tmp = self.peep
self.peep = None
return tmp
else:
return self.g.next()
def back(self, v):
self.peep = v
def tokenize(text):
a = ""
for c in text:
if c in ",()|":
if a:
yield a
yield c
a = ""
else:
a = a + c
if a:
yield a
def dtmf_etc(client_yate, server_yate, testid, targetid, callid, remoteid, test):
"""
Defines incall activity. Activity is defined in a string, according
to the following rules:
1-9,*,# - sends x as dtmf
connected? - waits for test.checkpoint message with check==connected
connected?x - waits for test.checkpoint message with check==connected for x secs
error! - raise exception on test.checkpoint message with check==error
s:x,c:y - does chan.attach with source==s and consumer==c
_ - 1s break, _*x - x times 1s
... - ends tester thread but does not drop connection
Example: _,1,1? - waits 1 second, sends 1, waits for 1 checkpoint.
"""
end = [server_yate.onwatch("chan.hangup", lambda m : m["id"] == remoteid)]
ignore_hangup = [False]
def one_step(step):
if "c:" in step or "s:" in step:
media = step.split("|")
consumer = None
source = None
for m in media:
if m.startswith("c:"):
consumer = m[2:]
elif m.startswith("s:"):
source = m[2:]
if source or consumer:
attrs = {"message": "chan.attach",
"id": callid}
if source:
attrs["source"] = source
if consumer:
attrs["consumer"] = consumer
client_yate.msg("chan.masquerade", attrs).enqueue()
elif step == "...":
logger.debug("[%d] call %s extended" % (testid, callid))
#tester.extended_calls.append(callid)
return
elif "?" in step:
i = step.find("?")
check = step[:i]
timeout = None
if len(step) > i + 1:
timeout = int(step[i+1:])
check_def = server_yate.onwatch(
"test.checkpoint",
lambda m : m["id"] == remoteid and m["check"] == check,
until = end[0])
if timeout:
logger.debug(
"[%d] waiting for: %ds for checkpoint: %s on: %s" % \
(testid, timeout, check, remoteid))
yield XOR(check_def, sleep(timeout))
what, _ = getResult()
if what > 0:
logger.warn(
"[%d] timeout while waiting %d s for checkpoint: %s on: %s" % \
(testid, timeout, check, remoteid))
## client_yate.msg("call.drop", {"id": callid}).enqueue()
## logger.debug(
## "[%d] dropping connection because of timeout: %s" % (testid, remoteid))
raise Exception("Timeout while waiting %d s for checkpoint: %s on: %s" % \
(timeout, check, remoteid))
logger.debug(
"[%d] checkpoint: %s on: %s passed" % \
(testid, check, remoteid))
else:
logger.debug(
"[%d] Waiting for checkpoint: '%s' on: %s" % \
(testid, check, remoteid))
yield check_def
getResult()
elif "!" in step:
check = step[:-1]
error = server_yate.onwatch(
"test.checkpoint",
lambda m : m["id"] == remoteid and m["check"] == check,
until = end[0])
end[0] = OR(end[0], error)
elif step in ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "#", "*"]:
logger.debug("[%d] Sending dtmf: %s to %s" % (testid, step, targetid))
client_yate.msg(
"chan.dtmf",
{"id": callid, "targetid": targetid, "text": step}).enqueue()
elif "&" in step:
timeout = int(step[1:])
ignore_hangup[0] = True
logger.debug("[%d] Waiting for 'end' checkpoint after hangup for: %d s." % (testid, timeout))
yield OR(end[0], sleep(timeout), patient = False)
getResult()
raise AbandonedException("Timeout while waiting for end.")
else:
t = 1
if "*" in step:
try:
t = int(step[2:])
except ValueError:
pass
logger.debug("[%d] Sleeping for: %d s." % (testid, t))
yield OR(end[0], sleep(t), patient = False)
getResult()
def parse(toparse):
last = None
for t in toparse:
if t == ",":
def x(a, b):
yield go(a)
getResult()
yield go(b)
getResult()
last = (x(last, parse(toparse)))
elif t == "(":
last = parse(toparse)
if toparse.next() != ")":
raise Excepion("Unmatched bracket!")
elif t == ")":
toparse.back(t)
break
elif t == "|":
def x(a, b):
yield OR(go(a), go(b))
r = getResult()
last = x(last, parse(toparse))
else:
last = one_step(t)
if last:
return last
else:
return None
try:
yield go(parse(peep(tokenize(test))))
getResult()
yield sleep(0.1)
getResult()
except Exception, e:
logger.info("[%d] dropping: %s" % (testid, callid))
d = OR(client_yate.msg("call.drop", {"id": callid}).dispatch(),
server_yate.msg("call.drop", {"id": remoteid}).dispatch())
if type(e) == type(AbandonedException("")) and ignore_hangup[0]:
yield server_yate.onwatch(
"test.checkpoint",
lambda m : m["id"] == remoteid and m["check"] == "end",
until = sleep(1))
getResult()
logger.debug("[%d] call %s finished" % (testid, callid))
return
yield d
getResult()
logger.debug("[%d] call %s finished" % (testid, callid))
raise e
logger.debug("[%d] dropping: %s" % (testid, callid))
yield client_yate.msg("call.drop", {"id": callid}).dispatch()
getResult()
yield server_yate.msg("call.drop", {"id": remoteid}).dispatch()
getResult()
logger.debug("[%d] call %s finished" % (testid, callid))