fixeria has submitted this change. ( https://gerrit.osmocom.org/c/python/osmo-python-tests/+/41055?usp=email )
Change subject: Drop osmopy/twisted_ipa.py and scripts/{ctrl2cgi.py,soap.py} ...................................................................... Drop osmopy/twisted_ipa.py and scripts/{ctrl2cgi.py,soap.py} scripts/{ctrl2cgi.py,soap.py} have been deprecated and scheduled for removal back in 2019. These scripts have been replaced by scripts/osmo_trap2cgi.py, which no longer needs Twisted. Change-Id: I848f60270dd683a56f56117c328d9961ac678d4b Related: 684388f ("Add initial version of asyncio trap2cgi script") Related: 7230f68 ("Mark soap.py as deprecated") --- M README D contrib/systemd/osmo-ctrl2cgi.service M debian/control D osmopy/twisted_ipa.py D scripts/ctrl2cgi.py D scripts/soap.py 6 files changed, 1 insertion(+), 691 deletions(-) Approvals: Jenkins Builder: Verified laforge: Looks good to me, but someone else must approve pespin: Looks good to me, approved diff --git a/README b/README index f325ae6..3b59257 100644 --- a/README +++ b/README @@ -15,8 +15,6 @@ Use: There are currently following scripts in this package: osmotestconfig.py - test that apps start/write with example configs -soap.py - implementation of SOAP <-> Ctrl proxy implemented on top of Twisted (deprecated, unmaintained) -ctrl2cgi.py - implementation of CGI <-> Ctrl proxy implemented on top of Twisted (deprecated, unmaintained) osmo_trap2cgi.py - implementation of CGI <-> Ctrl proxy implemented on top of asyncio and aiohttp osmo_rate_ctr2csv.py - rate counter dumper on top of osmo_ipa osmo_interact_vty.py - pipe stdin/stdout to a VTY session diff --git a/contrib/systemd/osmo-ctrl2cgi.service b/contrib/systemd/osmo-ctrl2cgi.service deleted file mode 100644 index da1faa7..0000000 --- a/contrib/systemd/osmo-ctrl2cgi.service +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=Proxy between given GCI service and Osmocom CTRL protocol -After=network-online.target -Wants=network-online.target - -[Service] -Type=simple -Restart=always -ExecStart=/usr/bin/ctrl2cgi.py -d -c /etc/osmocom/ctrl2cgi.ini -RestartSec=2 - -[Install] -WantedBy=multi-user.target diff --git a/debian/control b/debian/control index e02fd77..a1a272a 100644 --- a/debian/control +++ b/debian/control @@ -17,7 +17,7 @@ Package: python3-osmopy-utils Architecture: all -Depends: ${python3:Depends}, ${misc:Depends}, python3-osmopy-libs, python3-twisted, python3-aiohttp +Depends: ${python3:Depends}, ${misc:Depends}, python3-osmopy-libs, python3-aiohttp Description: Python code (not only) for testing of Osmocom programs . This package contains the Python 3 version of osmopy utils. diff --git a/osmopy/twisted_ipa.py b/osmopy/twisted_ipa.py deleted file mode 100755 index fd3cee5..0000000 --- a/osmopy/twisted_ipa.py +++ /dev/null @@ -1,384 +0,0 @@ -#!/usr/bin/env python3 -# -*- mode: python-mode; py-indent-tabs-mode: nil -*- -""" -/* - * Copyright (C) 2016 sysmocom s.f.m.c. GmbH - * - * All Rights Reserved - * - * 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 3 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. - */ -""" - -__version__ = "0.7.1" # bump this on every non-trivial change - -from osmopy.osmo_ipa import Ctrl, IPA -from twisted.internet.protocol import ReconnectingClientFactory -from twisted.internet import reactor -from twisted.protocols import basic -import argparse, logging, sys - -class IPACommon(basic.Int16StringReceiver): - """ - Generic IPA protocol handler: include some routines for simpler subprotocols. - It's not intended as full implementation of all subprotocols, rather common ground and example code. - """ - def dbg(self, line): - """ - Debug print helper - """ - self.factory.log.debug(line) - - def osmo_CTRL(self, data): - """ - OSMO CTRL protocol - Placeholder, see corresponding derived class - """ - pass - - def osmo_MGCP(self, data): - """ - OSMO MGCP extension - """ - self.dbg('OSMO MGCP received %s' % data) - - def osmo_LAC(self, data): - """ - OSMO LAC extension - """ - self.dbg('OSMO LAC received %s' % data) - - def osmo_SMSC(self, data): - """ - OSMO SMSC extension - """ - self.dbg('OSMO SMSC received %s' % data) - - def osmo_ORC(self, data): - """ - OSMO ORC extension - """ - self.dbg('OSMO ORC received %s' % data) - - def osmo_GSUP(self, data): - """ - OSMO GSUP extension - """ - self.dbg('OSMO GSUP received %s' % data) - - def osmo_OAP(self, data): - """ - OSMO OAP extension - """ - self.dbg('OSMO OAP received %s' % data) - - def osmo_RSPRO(self, data): - """ - OSMO RSPRO (Remote Sim Protocol) extension - """ - self.dbg('OSMO RSPRO received %s' % data) - - def osmo_UNKNOWN(self, data): - """ - OSMO defaul extension handler - """ - self.dbg('OSMO unknown extension received %s' % data) - - def handle_RSL(self, data, proto, extension): - """ - RSL protocol handler - """ - self.dbg('IPA RSL received message with extension %s' % extension) - - def handle_CCM(self, data, proto, msgt): - """ - CCM (IPA Connection Management) - Placeholder, see corresponding derived class - """ - pass - - def handle_SCCP(self, data, proto, extension): - """ - SCCP protocol handler - """ - self.dbg('IPA SCCP received message with extension %s' % extension) - - def handle_OML(self, data, proto, extension): - """ - OML protocol handler - """ - self.dbg('IPA OML received message with extension %s' % extension) - - def handle_OSMO(self, data, proto, extension): - """ - Dispatcher point for OSMO subprotocols based on extension name, lambda default should never happen - """ - method = getattr(self, 'osmo_' + IPA().ext(extension), lambda: "extension dispatch failure") - method(data) - - def handle_MGCP(self, data, proto, extension): - """ - MGCP protocol handler - """ - self.dbg('IPA MGCP received message with attribute %s' % extension) - - def handle_UNKNOWN(self, data, proto, extension): - """ - Default protocol handler - """ - self.dbg('IPA received message for %s (%s) protocol with attribute %s' % (IPA().proto(proto), proto, extension)) - - def process_chunk(self, data): - """ - Generic message dispatcher for IPA (sub)protocols based on protocol name, lambda default should never happen - """ - (_, proto, extension, content) = IPA().del_header(data) - if content is not None: - method = getattr(self, 'handle_' + IPA().proto(proto), lambda: "protocol dispatch failure") - method(content, proto, extension) - - def dataReceived(self, data): - """ - Override for dataReceived from Int16StringReceiver because of inherently incompatible interpretation of length - If default handler is used than we would always get off-by-1 error (Int16StringReceiver use equivalent of l + 2) - """ - if len(data): - (head, tail) = IPA().split_combined(data) - self.process_chunk(head) - self.dataReceived(tail) - - def connectionMade(self): - """ - We have to resetDelay() here to drop internal state to default values to make reconnection logic work - Make sure to call this via super() if overriding to keep reconnection logic intact - """ - addr = self.transport.getPeer() - self.dbg('IPA connected to %s:%d peer' % (addr.host, addr.port)) - self.factory.resetDelay() - - -class CCM(IPACommon): - """ - Implementation of CCM protocol for IPA multiplex - """ - def ack(self): - self.transport.write(IPA().id_ack()) - - def ping(self): - self.transport.write(IPA().ping()) - - def pong(self): - self.transport.write(IPA().pong()) - - def handle_CCM(self, data, proto, msgt): - """ - CCM (IPA Connection Management) - Only basic logic necessary for tests is implemented (ping-pong, id ack etc) - """ - if msgt == IPA.MSGT['ID_GET']: - self.transport.getHandle().sendall(IPA().id_resp(self.factory.ccm_id)) - # if we call - # self.transport.write(IPA().id_resp(self.factory.test_id)) - # instead, than we would have to also call - # reactor.callLater(1, self.ack) - # instead of self.ack() - # otherwise the writes will be glued together - hence the necessity for ugly hack with 1s timeout - # Note: this still might work depending on the IPA implementation details on the other side - self.ack() - # schedule PING in 4s - reactor.callLater(4, self.ping) - if msgt == IPA.MSGT['PING']: - self.pong() - - -class CTRL(IPACommon): - """ - Implementation of Osmocom control protocol for IPA multiplex - """ - def ctrl_SET(self, data, op_id, v): - """ - Handle CTRL SET command - """ - self.dbg('CTRL SET [%s] %s' % (op_id, v)) - - def ctrl_SET_REPLY(self, data, op_id, v): - """ - Handle CTRL SET reply - """ - self.dbg('CTRL SET REPLY [%s] %s' % (op_id, v)) - - def ctrl_GET(self, data, op_id, v): - """ - Handle CTRL GET command - """ - self.dbg('CTRL GET [%s] %s' % (op_id, v)) - - def ctrl_GET_REPLY(self, data, op_id, v): - """ - Handle CTRL GET reply - """ - self.dbg('CTRL GET REPLY [%s] %s' % (op_id, v)) - - def ctrl_TRAP(self, data, op_id, v): - """ - Handle CTRL TRAP command - """ - self.dbg('CTRL TRAP [%s] %s' % (op_id, v)) - - def ctrl_ERROR(self, data, op_id, v): - """ - Handle CTRL ERROR reply - """ - self.dbg('CTRL ERROR [%s] %s' % (op_id, v)) - - def osmo_CTRL(self, data): - """ - OSMO CTRL message dispatcher, lambda default should never happen - For basic tests only, appropriate handling routines should be replaced: see CtrlServer for example - """ - (cmd, op_id, v) = data.decode('utf-8').split(' ', 2) - method = getattr(self, 'ctrl_' + cmd, lambda: "CTRL unknown command") - method(data, op_id, v) - - -class IPAServer(CCM): - """ - Test implementation of IPA server - Demonstrate CCM opearation by overriding necessary bits from CCM - """ - def connectionMade(self): - """ - Keep reconnection logic working by calling routine from CCM - Initiate CCM upon connection - """ - addr = self.transport.getPeer() - self.factory.log.info('IPA server: connection from %s:%d client' % (addr.host, addr.port)) - super(IPAServer, self).connectionMade() - self.transport.write(IPA().id_get()) - - -class CtrlServer(CTRL): - """ - Test implementation of CTRL server - Demonstarte CTRL handling by overriding simpler routines from CTRL - """ - def connectionMade(self): - """ - Keep reconnection logic working by calling routine from CTRL - Send TRAP upon connection - Note: we can't use sendString() because of it's incompatibility with IPA interpretation of length prefix - """ - addr = self.transport.getPeer() - self.factory.log.info('CTRL server: connection from %s:%d client' % (addr.host, addr.port)) - super(CtrlServer, self).connectionMade() - self.transport.write(Ctrl().trap('LOL', 'what')) - self.transport.write(Ctrl().trap('rulez', 'XXX')) - - def reply(self, r): - self.transport.write(Ctrl().add_header(r)) - - def ctrl_SET(self, data, op_id, v): - """ - CTRL SET command: always succeed - """ - self.dbg('SET [%s] %s' % (op_id, v)) - self.reply('SET_REPLY %s %s' % (op_id, v)) - - def ctrl_GET(self, data, op_id, v): - """ - CTRL GET command: always fail - """ - self.dbg('GET [%s] %s' % (op_id, v)) - self.reply('ERROR %s No variable found' % op_id) - - -class IPAFactory(ReconnectingClientFactory): - """ - Generic IPA Client Factory which can be used to store state for various subprotocols and manage connections - Note: so far we do not really need separate Factory for acting as a server due to protocol simplicity - """ - protocol = IPACommon - log = None - ccm_id = IPA().identity(unit=b'1515/0/1', mac=b'b0:0b:fa:ce:de:ad:be:ef', utype=b'sysmoBTS', name=b'StingRay', location=b'hell', sw=IPA.version.encode('utf-8')) - - def __init__(self, proto=None, log=None, ccm_id=None): - if proto: - self.protocol = proto - if ccm_id: - self.ccm_id = ccm_id - if log: - self.log = log - else: - self.log = logging.getLogger('IPAFactory') - self.log.setLevel(logging.CRITICAL) - self.log.addHandler(logging.NullHandler) - - def clientConnectionFailed(self, connector, reason): - """ - Only necessary for as debugging aid - if we can somehow set parent's class noisy attribute then we can omit this method - """ - self.log.warning('IPAFactory connection failed: %s' % reason.getErrorMessage()) - ReconnectingClientFactory.clientConnectionFailed(self, connector, reason) - - def clientConnectionLost(self, connector, reason): - """ - Only necessary for as debugging aid - if we can somehow set parent's class noisy attribute then we can omit this method - """ - self.log.warning('IPAFactory connection lost: %s' % reason.getErrorMessage()) - ReconnectingClientFactory.clientConnectionLost(self, connector, reason) - - -if __name__ == '__main__': - p = argparse.ArgumentParser("Twisted IPA (module v%s) app" % IPA.version) - p.add_argument('-v', '--version', action='version', version="%(prog)s v" + __version__) - p.add_argument('-p', '--port', type=int, default=4250, help="Port to use for CTRL interface") - p.add_argument('-d', '--host', default='localhost', help="Adress to use for CTRL interface") - cs = p.add_mutually_exclusive_group() - cs.add_argument("-c", "--client", action='store_true', help="asume client role") - cs.add_argument("-s", "--server", action='store_true', help="asume server role") - ic = p.add_mutually_exclusive_group() - ic.add_argument("--ipa", action='store_true', help="use IPA protocol") - ic.add_argument("--ctrl", action='store_true', help="use CTRL protocol") - args = p.parse_args() - test = False - - log = logging.getLogger('TwistedIPA') - log.setLevel(logging.DEBUG) - log.addHandler(logging.StreamHandler(sys.stdout)) - - if args.ctrl: - if args.client: - # Start osmo-bsc to receive TRAP messages when osmo-bts-* connects to it - print('CTRL client, connecting to %s:%d' % (args.host, args.port)) - reactor.connectTCP(args.host, args.port, IPAFactory(CTRL, log)) - test = True - if args.server: - # Use bsc_control.py to issue set/get commands - print('CTRL server, listening on port %d' % args.port) - reactor.listenTCP(args.port, IPAFactory(CtrlServer, log)) - test = True - if args.ipa: - if args.client: - # Start osmo-nitb which would initiate A-bis/IP session - print('IPA client, connecting to %s ports %d and %d' % (args.host, IPA.TCP_PORT_OML, IPA.TCP_PORT_RSL)) - reactor.connectTCP(args.host, IPA.TCP_PORT_OML, IPAFactory(CCM, log)) - reactor.connectTCP(args.host, IPA.TCP_PORT_RSL, IPAFactory(CCM, log)) - test = True - if args.server: - # Start osmo-bts-* which would attempt to connect to us - print('IPA server, listening on ports %d and %d' % (IPA.TCP_PORT_OML, IPA.TCP_PORT_RSL)) - reactor.listenTCP(IPA.TCP_PORT_RSL, IPAFactory(IPAServer, log)) - reactor.listenTCP(IPA.TCP_PORT_OML, IPAFactory(IPAServer, log)) - test = True - if test: - reactor.run() - else: - print("Please specify which protocol in which role you'd like to test.") diff --git a/scripts/ctrl2cgi.py b/scripts/ctrl2cgi.py deleted file mode 100755 index 9f66f99..0000000 --- a/scripts/ctrl2cgi.py +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/env python3 -# -*- mode: python-mode; py-indent-tabs-mode: nil -*- -""" -/* - * Copyright (C) 2018 sysmocom s.f.m.c. GmbH - * - * All Rights Reserved - * - * 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 3 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. - */ -""" - -__version__ = "0.1.1" # bump this on every non-trivial change - -import argparse, os, logging, logging.handlers, datetime -import hashlib -import json -import configparser -from functools import partial -from distutils.version import StrictVersion as V -from twisted.internet import defer, reactor -from treq import post, collect -from osmopy.trap_helper import debug_init, get_type, get_r, p_h, gen_hash, make_params, comm_proc -from osmopy.twisted_ipa import CTRL, IPAFactory, __version__ as twisted_ipa_version -from osmopy.osmo_ipa import Ctrl - -# we don't support older versions of TwistedIPA module -assert V(twisted_ipa_version) > V('0.4') - -def log_duration(log, bid, ts, ts_http): - """ - Log human-readable duration from timestamps - """ - base = datetime.datetime.now() - delta_t = datetime.timedelta(seconds = (base - ts).total_seconds()) - delta_h = datetime.timedelta(seconds = (base - ts_http).total_seconds()) - delta_w = delta_t - delta_h - log.debug('Request for BSC %s took %s total (%s wait, %s http)' % (bid, delta_t, delta_w, delta_h)) - -def handle_reply(ts, ts_http, bid, f, log, resp): - """ - Reply handler: process raw CGI server response, function f to run for each command - """ - decoded = json.loads(resp.decode('utf-8')) - log_duration(log, bid, ts, ts_http) - comm_proc(decoded.get('commands'), bid, f, log) - -def make_async_req(ts, dst, par, f_write, f_log, tout): - """ - Assemble deferred request parameters and partially instantiate response handler - """ - d = post(dst, par, timeout=tout) - d.addCallback(collect, partial(handle_reply, ts, datetime.datetime.now(), par['bsc_id'], f_write, f_log)) - d.addErrback(lambda e: f_log.critical("HTTP POST error %s while trying to register BSC %s on %s (timeout %d)" % (repr(e), par['bsc_id'], dst, tout))) # handle HTTP errors - return d - -class Trap(CTRL): - """ - TRAP handler (agnostic to factory's client object) - """ - def ctrl_TRAP(self, data, op_id, v): - """ - Parse CTRL TRAP and dispatch to appropriate handler after normalization - """ - if get_type(v) == 'location-state': - p = p_h(v) - self.handle_locationstate(p(1), p(3), p(5), p(7), get_r(v)) - else: - self.factory.log.debug('Ignoring TRAP %s' % (v.split()[0])) - - def ctrl_SET_REPLY(self, data, _, v): - """ - Debug log for replies to our commands - """ - self.factory.log.debug('SET REPLY %s' % v) - - def ctrl_ERROR(self, data, op_id, v): - """ - We want to know if smth went wrong - """ - self.factory.log.debug('CTRL ERROR [%s] %s' % (op_id, v)) - - def connectionMade(self): - """ - Logging wrapper, calling super() is necessary not to break reconnection logic - """ - self.factory.log.info("Connected to CTRL@%s:%d" % (self.factory.addr_ctrl, self.factory.port_ctrl)) - super(CTRL, self).connectionMade() - - def handle_locationstate(self, net, bsc, bts, trx, data): - """ - Handle location-state TRAP: parse trap content, build CGI Request and use treq's routines to post it while setting up async handlers - """ - params = make_params(bsc, data) - self.factory.log.info('location-state@%s.%s.%s.%s (%s) => %s' % (net, bsc, bts, trx, params['time_stamp'], data)) - params['h'] = gen_hash(params, self.factory.secret_key) - t = datetime.datetime.now() - self.factory.log.debug('Preparing request for BSC %s @ %s...' % (params['bsc_id'], t)) - # Ensure that we run only limited number of requests in parallel: - self.factory.semaphore.run(make_async_req, t, self.factory.location, params, self.transport.write, self.factory.log, self.factory.timeout) - - -class TrapFactory(IPAFactory): - """ - Store CGI information so TRAP handler can use it for requests - """ - def __init__(self, proto, log): - self.log = log - level = self.log.getEffectiveLevel() - self.log.setLevel(logging.WARNING) # we do not need excessive debug from lower levels - super(TrapFactory, self).__init__(proto, self.log) - self.log.setLevel(level) - self.log.debug("Using Osmocom IPA library v%s" % Ctrl.version) - - -if __name__ == '__main__': - p = argparse.ArgumentParser(description='Proxy between given GCI service and Osmocom CTRL protocol.') - p.add_argument('-v', '--version', action='version', version=("%(prog)s v" + __version__)) - p.add_argument('-d', '--debug', action='store_true', help="Enable debug log") # keep in sync with debug_init call below - p.add_argument('-c', '--config-file', required=True, help="Path to mandatory config file (in INI format).") - args = p.parse_args(namespace=TrapFactory) - - log = debug_init('CTRL2CGI', args.debug) - - T = TrapFactory(Trap, log) - - config = configparser.ConfigParser(interpolation=None) - config.read(args.config_file) - - T.addr_ctrl = config['main'].get('addr_ctrl', 'localhost') - T.port_ctrl = config['main'].getint('port_ctrl', 4250) - T.timeout = config['main'].getint('timeout', 30) - T.semaphore = defer.DeferredSemaphore(config['main'].getint('num_max_conn', 5)) - T.location = config['main'].get('location') - T.secret_key = config['main'].get('secret_key') - - log.info("CGI proxy v%s starting with PID %d:" % (__version__, os.getpid())) - log.info("destination %s (concurrency %d)" % (T.location, T.semaphore.limit)) - log.info("connecting to %s:%d..." % (T.addr_ctrl, T.port_ctrl)) - reactor.connectTCP(T.addr_ctrl, T.port_ctrl, T) - reactor.run() diff --git a/scripts/soap.py b/scripts/soap.py deleted file mode 100755 index f054ef3..0000000 --- a/scripts/soap.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/env python3 -# -*- mode: python-mode; py-indent-tabs-mode: nil -*- -""" -/* - * Copyright (C) 2016 sysmocom s.f.m.c. GmbH - * - * All Rights Reserved - * - * 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 3 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. - */ -""" - -__version__ = "0.7.2" # bump this on every non-trivial change - -import argparse, os, logging -from functools import partial -from distutils.version import StrictVersion as V # FIXME: use NormalizedVersion from PEP-386 when available -from twisted.internet import defer, reactor -from suds.client import Client -from treq import post, collect -from osmopy.trap_helper import debug_init, get_type, get_r, p_h, make_params, comm_proc -from osmopy.twisted_ipa import CTRL, IPAFactory, __version__ as twisted_ipa_version -from osmopy.osmo_ipa import Ctrl - -# we don't support older versions of TwistedIPA module -assert V(twisted_ipa_version) > V('0.4') - - -def handle_reply(p, bid, f, log, r): - """ - Reply handler: takes function p to process raw SOAP server reply r, function f to run for each command - """ - repl = p(r) # result is expected to have both commands[] array and error string (could be None) - bsc_id = comm_proc(repl.commands, bid, f, log) - log.info("Received SOAP response for BSC %s with %d commands, error status: %s" % (bsc_id, len(repl.commands), repl.error)) - - -class Trap(CTRL): - """ - TRAP handler (agnostic to factory's client object) - """ - def ctrl_TRAP(self, data, op_id, v): - """ - Parse CTRL TRAP and dispatch to appropriate handler after normalization - """ - self.factory.log.debug('TRAP %s' % v) - t_type = get_type(v) - p = p_h(v) - method = getattr(self, 'handle_' + t_type.replace('-', ''), lambda *_: "Unhandled %s trap" % t_type) - method(p(1), p(3), p(5), p(7), get_r(v)) - - def ctrl_SET_REPLY(self, data, _, v): - """ - Debug log for replies to our commands - """ - self.factory.log.debug('SET REPLY %s' % v) - - def ctrl_ERROR(self, data, op_id, v): - """ - We want to know if smth went wrong - """ - self.factory.log.debug('CTRL ERROR [%s] %s' % (op_id, v)) - - def connectionMade(self): - """ - Logging wrapper, calling super() is necessary not to break reconnection logic - """ - self.factory.log.info("Connected to CTRL@%s:%d" % (self.factory.host, self.factory.port)) - super(CTRL, self).connectionMade() - - @defer.inlineCallbacks - def handle_locationstate(self, net, bsc, bts, trx, data): - """ - Handle location-state TRAP: parse trap content, build SOAP context and use treq's routines to post it while setting up async handlers - """ - params = make_params(bsc, data) - self.factory.log.info('location-state@%s.%s.%s.%s (%s) => %s' % (net, bsc, bts, trx, params['time_stamp'], data)) - ctx = self.factory.client.registerSiteLocation(bsc, float(params['lon']), float(params['lat']), params['position_validity'], params['time_stamp'], params['oper_status'], params['admin_status'], params['policy_status']) - d = post(self.factory.location, ctx.envelope) - d.addCallback(collect, partial(handle_reply, ctx.process_reply, params['bsc_id'], self.transport.write, self.factory.log)) # treq's collect helper is handy to get all reply content at once using closure on ctx - d.addErrback(lambda e, bsc: self.factory.log.critical("HTTP POST error %s while trying to register BSC %s on %s" % (repr(e), bsc, self.factory.location)), bsc) # handle HTTP errors - # Ensure that we run only limited number of requests in parallel: - yield self.factory.semaphore.acquire() - yield d # we end up here only if semaphore is available which means it's ok to fire the request without exceeding the limit - self.factory.semaphore.release() - - def handle_notificationrejectionv1(self, net, bsc, bts, trx, data): - """ - Handle notification-rejection-v1 TRAP: just an example to show how more message types can be handled - """ - self.factory.log.debug('notification-rejection-v1@bsc-id %s => %s' % (bsc, data)) - - -class TrapFactory(IPAFactory): - """ - Store SOAP client object so TRAP handler can use it for requests - """ - location = None - log = None - semaphore = None - client = None - host = None - port = None - def __init__(self, host, port, proto, semaphore, log, wsdl=None, location=None): - self.host = host # for logging only, - self.port = port # seems to be no way to get it from ReconnectingClientFactory - self.log = log - self.semaphore = semaphore - soap = Client(wsdl, location=location, nosend=True) # make async SOAP client - self.location = location.encode() if location else soap.wsdl.services[0].ports[0].location # necessary for dispatching HTTP POST via treq - self.client = soap.service - level = self.log.getEffectiveLevel() - self.log.setLevel(logging.WARNING) # we do not need excessive debug from lower levels - super(TrapFactory, self).__init__(proto, self.log) - self.log.setLevel(level) - self.log.debug("Using IPA %s, SUDS client: %s" % (Ctrl.version, soap)) - - -if __name__ == '__main__': - p = argparse.ArgumentParser(description='Proxy between given SOAP service and Osmocom CTRL protocol.') - p.add_argument('-v', '--version', action='version', version=("%(prog)s v" + __version__)) - p.add_argument('-p', '--port', type=int, default=4250, help="Port to use for CTRL interface, defaults to 4250") - p.add_argument('-c', '--ctrl', default='localhost', help="Adress to use for CTRL interface, defaults to localhost") - p.add_argument('-w', '--wsdl', required=True, help="WSDL URL for SOAP") - p.add_argument('-n', '--num', type=int, default=5, help="Max number of concurrent HTTP requests to SOAP server") - p.add_argument('-d', '--debug', action='store_true', help="Enable debug log") # keep in sync with debug_init call below - p.add_argument('-l', '--location', help="Override location found in WSDL file (don't use unless you know what you're doing)") - args = p.parse_args() - - log = debug_init('CTRL2SOAP', args.debug) - - log.info("SOAP proxy %s starting with PID %d ..." % (__version__, os.getpid())) - reactor.connectTCP(args.ctrl, args.port, TrapFactory(args.ctrl, args.port, Trap, defer.DeferredSemaphore(args.num), log, args.wsdl, args.location)) - reactor.run() -- To view, visit https://gerrit.osmocom.org/c/python/osmo-python-tests/+/41055?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email Gerrit-MessageType: merged Gerrit-Project: python/osmo-python-tests Gerrit-Branch: master Gerrit-Change-Id: I848f60270dd683a56f56117c328d9961ac678d4b Gerrit-Change-Number: 41055 Gerrit-PatchSet: 2 Gerrit-Owner: fixeria <vyanits...@sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanits...@sysmocom.de> Gerrit-Reviewer: laforge <lafo...@osmocom.org> Gerrit-Reviewer: osmith <osm...@sysmocom.de> Gerrit-Reviewer: pespin <pes...@sysmocom.de>