This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.
laforge gerrit-no-reply at lists.osmocom.orglaforge has submitted this change. ( https://gerrit.osmocom.org/c/mncc-python/+/17403 )
Change subject: mncc_mt_loadgen.py: Program for MT call load testing with rtpsource
......................................................................
mncc_mt_loadgen.py: Program for MT call load testing with rtpsource
This program (derived from mncc_test.py) is used to start MT calls
via the MNCC interface of OsmoMSC. Every calls RTP is connected
to the new 'rtpsource' process, which generates a realistic RTP
flow in terms of number of packets (20ms interval) and payload size.
Change-Id: I9e5d799aaeeff5188dc97061f0d6e1873d9bf653
---
A ctrl.py
A mncc_mt_loadgen.py
2 files changed, 258 insertions(+), 0 deletions(-)
Approvals:
laforge: Looks good to me, approved; Verified
diff --git a/ctrl.py b/ctrl.py
new file mode 100644
index 0000000..6f51684
--- /dev/null
+++ b/ctrl.py
@@ -0,0 +1,70 @@
+# Simple class for synchronous/blocking interface with a remote
+# Osmocom program via the CTRL interface
+
+# (C) 2020 by Harald Welte <laforge at gnumonks.org>
+#
+# Licensed under GNU General Public License, Version 2 or at your
+# option, any later version.
+
+from osmopy.osmo_ipa import Ctrl
+import socket
+
+class OsmoCtrlSimple:
+ '''Simple class for synchronous/blocking interface with a remote
+ Osmocom program via the CTRL interface'''
+ remote_host = "127.0.0.1"
+ remote_port = 0
+ sock = None
+
+ def __init__(self, host=None, port=None):
+ self.remote_host = host
+ self.remote_port = port
+
+ def connect(self, host=None, port=None):
+ if host:
+ self.remote_host = host
+ if port:
+ self.remote_port = port
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.sock.setblocking(1)
+ self.sock.connect((self.remote_host, self.remote_port))
+ return self.sock
+
+ def disconnect(self):
+ self.sock.close()
+ self.sock = None
+
+ def _leftovers(self, fl):
+ """
+ Read outstanding data if any according to flags
+ """
+ try:
+ data = self.sock.recv(1024, fl)
+ except socket.error as _:
+ return False
+ if len(data) != 0:
+ tail = data
+ while True:
+ (head, tail) = Ctrl().split_combined(tail)
+ print("Got message:", Ctrl().rem_header(head))
+ if len(tail) == 0:
+ break
+ return True
+ return False
+
+ def do_set_get(self, var, value = None):
+ self._leftovers(socket.MSG_DONTWAIT)
+ (r, c) = Ctrl().cmd(var, value)
+ self.sock.send(c)
+ while True:
+ ret = self.sock.recv(4096)
+ # handle multiple messages, ignore TRAPs
+ ret = Ctrl().skip_traps(ret)
+ if ret != None:
+ (i, k, v) = Ctrl().parse(ret)
+ break;
+ return (Ctrl().rem_header(ret),) + Ctrl().verify(ret, r, var, value)
+
+ def set_var(self, var, val):
+ (a, _, _) = self.do_set_get(var, val)
+ return a
diff --git a/mncc_mt_loadgen.py b/mncc_mt_loadgen.py
new file mode 100755
index 0000000..a7daabf
--- /dev/null
+++ b/mncc_mt_loadgen.py
@@ -0,0 +1,188 @@
+#!/usr/bin/env python2
+
+# Python testing tool for establishing mobile-terminated calls using
+# the OsmoMSC MNCC interface. It wants to communicate via CTRL with a
+# 'rtpsoucre' program which will take care of generating RtP flows
+# for the MT calls that are established.
+#
+# (C) 2015, 2020 by Harald Welte <laforge at gnumonks.org>
+#
+# Licensed under GNU General Public License, Version 2 or at your
+# option, any later version.
+
+RTPSOURCE_CTRL_IP = "127.0.0.1"
+RTPSOURCE_CTRL_PORT = 11111
+
+
+from gsm_call_fsm import GsmCallFsm, GSM48
+from mncc_sock import MnccSocket
+from thread import start_new_thread
+from ctrl import OsmoCtrlSimple
+import pykka
+import logging as log
+import signal, sys, time
+import readline, code
+import socket
+import struct
+import mncc
+from mncc_sock import mncc_rtp_msg
+
+def int2ipstr(addr):
+ '''Convert from an integer to a strinf-formatted IPv4 address'''
+ return socket.inet_ntoa(struct.pack("!I", addr))
+
+def ipstr2int(addr):
+ '''Convert from a string-formatted IPv4 address to integer'''
+ return struct.unpack("!I", socket.inet_aton(addr))[0]
+
+
+class MnccActor(pykka.ThreadingActor):
+ '''MnccActor provides an interface for GsmCallFsm to send MNCC messages'''
+ def __init__(self, mncc_sock):
+ super(MnccActor, self).__init__()
+ self.mncc_sock = mncc_sock
+
+ def on_receive(self, message):
+ if message['type'] == 'send':
+ msg = message['msg']
+ log.debug('MnccActor TxMNCC %s' % msg)
+ mncc_sock.send(msg)
+ else:
+ raise Exception('mncc', 'MnccActor Received unhandled %s' % message)
+
+def mncc_rx_thread(mncc_sock):
+ '''MNCC receive thread, broadcasting received MNCC packets to GsmCallFsm'''
+ while 1:
+ msg = mncc_sock.recv()
+ if msg == None:
+ raise Exception('mncc', 'MnccSocket disconnected')
+
+ if msg.is_frame():
+ log.warning("Dropping traffic frame: %s" % msg)
+ continue
+
+ log.debug("MnccActor RxMNCC %s, broadcasting to Call FSMs" % msg)
+ # we simply broadcast to all calls
+ pykka.ActorRegistry.broadcast({'type': 'mncc', 'msg': msg}, GsmCallFsm)
+
+
+class RtpSourceCtrlActor(pykka.ThreadingActor):
+ '''Actor for talking with the rtpsource program via an osmocom CTRL interface'''
+ def __init__(self, ctrl_host, ctrl_port=11111):
+ super(RtpSourceCtrlActor, self).__init__()
+ self.ctrl = OsmoCtrlSimple(ctrl_host, ctrl_port)
+ self.ctrl.connect()
+
+ def _set_var(self, var, val):
+ (res, var, val) = self.ctrl.do_set_get(var, val)
+ log.debug('RtpSourceCtrlActor result=%s var=%s val=%s' % (res, var, val))
+ return (res, var, val)
+
+ def on_receive(self, message):
+ if message['type'] == 'rtp_create':
+ (res, var, val) = self._set_var('rtp_create', message['cname'])
+ v = val.split(',') # input looks like '1,127.23.23.23,37723'
+ return {'cname': v[0], 'remote_host': v[1], 'remote_port': int(v[2])}
+ elif message['type'] == 'rtp_connect':
+ val = '%s,%s,%u,%u' % (message['cname'], message['remote_host'],
+ message['remote_port'], message['payload_type'])
+ (res, var, val) = self._set_var('rtp_connect', val)
+ return res
+ elif message['type'] == 'rtp_delete':
+ (res, var, val) = self._set_var('rtp_delete', message['cname'])
+ return res
+ else:
+ raise Exception('ctrl', 'RtpSourceCtrlActor Received unhandled %s' % message)
+
+
+class MTCallRtpsource(pykka.ThreadingActor):
+ '''Actor to start a network-initiated MT (mobile terminated) call to a given MSISDN,
+ connecting the RTP flow to an extenal rtpsource program'''
+ def __init__(self, mncc_act, ctrl_act, codecs_permitted = GSM48.AllCodecs):
+ super(MTCallRtpsource, self).__init__()
+ self.mncc_act = mncc_act
+ self.ctrl_act = ctrl_act
+ self.codecs_permitted = codecs_permitted
+ self.call = GsmCallFsm.start(self.mncc_act, self.actor_ref, True, self.codecs_permitted)
+ self.callref = self.call.ask({'type':'get_callref'})
+ self.state = 'NULL'
+ self.rtp_msc = None
+
+ def start_call(self, msisdn_called, msisdn_calling):
+ '''Start a MT call from given [external] calling party to given mobile party MSISDN'''
+ self.msisdn_called = msisdn_called
+ self.msisdn_calling = msisdn_calling
+ # allocate a RTP connection @ rtpsource
+ r = self.ctrl_act.ask({'type':'rtp_create', 'cname':self.callref})
+ self.ext_rtp_host = r['remote_host']
+ self.ext_rtp_port = r['remote_port']
+ # start the MNCC call FSM
+ self.call.tell({'type':'start_mt_call',
+ 'calling':self.msisdn_calling, 'called':self.msisdn_called})
+
+ def on_stop(self):
+ # Attempt to do a graceful shutdown by deleting the RTP connection from rtpsource
+ self.ctrl_act.ask({'type':'rtp_delete', 'cname':self.callref})
+ self.call.stop()
+
+ def on_failure(self, exception_type, exception_value, traceback):
+ # on_stop() is not called in case of failure; fix that
+ self.on_stop()
+
+ def on_receive(self, message):
+ if message['type'] == 'rtp_create_ind':
+ # MSC+MGW informs us of the PLMN side IP/port
+ mncc_rtp_ind = message['rtp']
+ # tell external rtpsourc to connect to this address
+ r = self.ctrl_act.ask({'type':'rtp_connect', 'cname':self.callref,
+ 'remote_host': int2ipstr(mncc_rtp_ind.ip),
+ 'remote_port': mncc_rtp_ind.port,
+ 'payload_type':3})
+ # tell MSC to connect to ip/port @ rtpsource
+ mncc_rtp_conn = mncc_rtp_msg(msg_type=mncc.MNCC_RTP_CONNECT, callref=self.callref,
+ ip=ipstr2int(self.ext_rtp_host), port=self.ext_rtp_port)
+ self.mncc_act.tell({'type': 'send', 'msg': mncc_rtp_conn})
+ elif message['type'] == 'call_state_change':
+ if message['new_state'] == 'NULL':
+ # Call FSM has reached the NULL state again (call terminated)
+ # on_stop() will clean up the RTP connection at rtpsource
+ self.stop()
+
+
+# capture SIGINT (Ctrl+C) and stop all actors
+def sigint_handler(signum, frame):
+ pykka.ActorRegistry.stop_all()
+ sys.exit(0)
+
+log.basicConfig(level = log.DEBUG,
+ format = "%(levelname)s %(filename)s:%(lineno)d %(message)s")
+
+signal.signal(signal.SIGINT, sigint_handler)
+
+# start the MnccSocket and associated pykka actor + rx thread
+mncc_sock = MnccSocket()
+mncc_act = MnccActor.start(mncc_sock)
+start_new_thread(mncc_rx_thread, (mncc_sock,))
+
+# connect via CTRL to rtpsource
+rtpctrl_act = RtpSourceCtrlActor.start(RTPSOURCE_CTRL_IP, RTPSOURCE_CTRL_PORT)
+
+# convenience wrapper
+def mt_call(msisdn_called, msisdn_calling = '123456789', codecs = GSM48.AllCodecs):
+ call_conn = MTCallRtpsource.start(mncc_act, rtpctrl_act, codecs).proxy()
+ call_conn.start_call(msisdn_called, msisdn_calling)
+ return call_conn
+
+# start one call automatically
+mt_call('90001')
+
+# start a shell to enable the user to add more calls as needed
+vars = globals().copy()
+vars.update(locals())
+shell = code.InteractiveConsole(vars)
+try:
+ shell.interact()
+except SystemExit:
+ pass
+# clan up after user leaves interactive shell
+sigint_handler(signal.SIGINT, None)
--
To view, visit https://gerrit.osmocom.org/c/mncc-python/+/17403
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: mncc-python
Gerrit-Branch: master
Gerrit-Change-Id: I9e5d799aaeeff5188dc97061f0d6e1873d9bf653
Gerrit-Change-Number: 17403
Gerrit-PatchSet: 3
Gerrit-Owner: laforge <laforge at osmocom.org>
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200308/2329ee45/attachment.htm>