Move common Trap-related code into separate file

The ctrl2cgi.py is heavily based upon soap.py - let's move all the
shared code into separate file to make further modifications easier.

Change-Id: I7b59f2dbded9074d15f2d2f40bf5a92ed02601e2
Related: SYS#4399
This commit is contained in:
Max 2018-11-26 16:42:59 +01:00
parent 98b993f010
commit 5baba8c078
5 changed files with 147 additions and 194 deletions

1
README
View File

@ -33,6 +33,7 @@ cd <your_source_dir>/openbsc/openbsc && osmodumpdoc.py
Libraries: Libraries:
osmopy/osmoutil.py - code that's shared between the scripts osmopy/osmoutil.py - code that's shared between the scripts
osmopy/osmo_ipa.py - generic implementation of IPA and Ctrl protocols in python osmopy/osmo_ipa.py - generic implementation of IPA and Ctrl protocols in python
osmopy/trap_helper.py - generic Trap class and related helpers used by soap.py and ctrl2cgi.py
osmopy/osmo_interact/{vty,ctrl}.py - general interactions with VTY and CTRL ports osmopy/osmo_interact/{vty,ctrl}.py - general interactions with VTY and CTRL ports
osmopy/obscvty.py - connect to a vty, superseded by osmo_interact/vty osmopy/obscvty.py - connect to a vty, superseded by osmo_interact/vty

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python
__version__ = '0.0.9' __version__ = '0.1.0'
__all__ = ['obscvty', 'osmoutil', 'osmo_ipa', 'osmo_interact', 'twisted_ipa'] __all__ = ['obscvty', 'osmoutil', 'osmo_ipa', 'osmo_interact', 'trap_helper', 'twisted_ipa']

126
osmopy/trap_helper.py Normal file
View File

@ -0,0 +1,126 @@
#!/usr/bin/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.
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
"""
import sys, os, signal, logging, logging.handlers
from functools import partial
from osmopy.twisted_ipa import CTRL
from twisted.internet import defer
# keys from OpenBSC openbsc/src/libbsc/bsc_rf_ctrl.c, values SOAP-specific
oper = { 'inoperational' : 0, 'operational' : 1 }
admin = { 'locked' : 0, 'unlocked' : 1 }
policy = { 'off' : 0, 'on' : 1, 'grace' : 2, 'unknown' : 3 }
# keys from OpenBSC openbsc/src/libbsc/bsc_vty.c
fix = { 'invalid' : 0, 'fix2d' : 1, 'fix3d' : 1 } # SOAP server treats it as boolean but expects int
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
"""
(l, r) = v.split()
loc = l.split('.')
t_type = loc[-1]
p = partial(lambda a, i: a[i] if len(a) > i else None, loc) # parse helper
method = getattr(self, 'handle_' + t_type.replace('-', ''), lambda: "Unhandled %s trap" % t_type)
method(p(1), p(3), p(5), p(7), r) # we expect net.0.bsc.666.bts.2.trx.1 format for trap prefix
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, prepare parameters and use treq's routines to post it while setting up async handlers
"""
(ts, fx, lat, lon, height, opr, adm, pol, mcc, mnc) = data.split(',')
tstamp = datetime.datetime.fromtimestamp(float(ts)).isoformat()
self.factory.log.debug('location-state@%s.%s.%s.%s (%s) [%s/%s] => %s' % (net, bsc, bts, trx, tstamp, mcc, mnc, data))
d = self.factory.prepare_params(bsc, lon, lat, fix.get(fx, 0), tstamp, oper.get(opr, 2), admin.get(adm, 2), policy.get(pol, 3))
d.addErrback(lambda e, bsc: self.factory.log.critical("HTTP POST error %s while trying to register BSC %s on %s" % (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))
def reloader(path, script, log, dbg1, dbg2, signum, _):
"""
Signal handler: we have to use execl() because twisted's reactor is not restartable due to some bug in twisted implementation
"""
log.info("Received Signal %d - restarting..." % signum)
if signum == signal.SIGUSR1 and dbg1 not in sys.argv and dbg2 not in sys.argv:
sys.argv.append(dbg1) # enforce debug
if signum == signal.SIGUSR2 and (dbg1 in sys.argv or dbg2 in sys.argv): # disable debug
if dbg1 in sys.argv:
sys.argv.remove(dbg1)
if dbg2 in sys.argv:
sys.argv.remove(dbg2)
os.execl(path, script, *sys.argv[1:])
def debug_init(name, is_debug, output):
log = logging.getLogger(name)
if is_debug:
log.setLevel(logging.DEBUG)
else:
log.setLevel(logging.INFO)
log.addHandler(logging.handlers.SysLogHandler('/dev/log'))
if output:
log.addHandler(logging.StreamHandler(sys.stdout))
reboot = partial(reloader, os.path.abspath(__file__), os.path.basename(__file__), log, '-d', '--debug') # keep in sync with caller's add_argument()
signal.signal(signal.SIGHUP, reboot)
signal.signal(signal.SIGQUIT, reboot)
signal.signal(signal.SIGUSR1, reboot) # restart and enabled debug output
signal.signal(signal.SIGUSR2, reboot) # restart and disable debug output
return log

View File

@ -29,6 +29,7 @@ from osmopy.twisted_ipa import CTRL, IPAFactory, __version__ as twisted_ipa_vers
from osmopy.osmo_ipa import Ctrl from osmopy.osmo_ipa import Ctrl
from treq import post, collect from treq import post, collect
from functools import partial from functools import partial
from osmopy.trap_helper import Trap, reloader, debug_init
from distutils.version import StrictVersion as V # FIXME: use NormalizedVersion from PEP-386 when available from distutils.version import StrictVersion as V # FIXME: use NormalizedVersion from PEP-386 when available
import argparse, datetime, signal, sys, os, logging, logging.handlers import argparse, datetime, signal, sys, os, logging, logging.handlers
import hashlib import hashlib
@ -38,13 +39,6 @@ import configparser
# we don't support older versions of TwistedIPA module # we don't support older versions of TwistedIPA module
assert V(twisted_ipa_version) > V('0.4') assert V(twisted_ipa_version) > V('0.4')
# keys from OpenBSC openbsc/src/libbsc/bsc_rf_ctrl.c, values CGI-specific
oper = { 'inoperational' : 0, 'operational' : 1 }
admin = { 'locked' : 0, 'unlocked' : 1 }
policy = { 'off' : 0, 'on' : 1, 'grace' : 2, 'unknown' : 3 }
# keys from OpenBSC openbsc/src/libbsc/bsc_vty.c
fix = { 'invalid' : 0, 'fix2d' : 1, 'fix3d' : 1 } # CGI server treats it as boolean but expects int
@defer.inlineCallbacks @defer.inlineCallbacks
def handle_reply(f, log, resp): def handle_reply(f, log, resp):
@ -81,65 +75,6 @@ def gen_hash(params, skey):
#print('HASH: \nparams="%r"\ninput="%s" \nres="%s"' %(params, input, res)) #print('HASH: \nparams="%r"\ninput="%s" \nres="%s"' %(params, input, res))
return res return res
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)
(l, r) = v.split()
loc = l.split('.')
t_type = loc[-1]
p = partial(lambda a, i: a[i] if len(a) > i else None, loc) # parse helper
method = getattr(self, 'handle_' + t_type.replace('-', ''), lambda *_: "Unhandled %s trap" % t_type)
method(p(1), p(3), p(5), p(7), r) # we expect net.0.bsc.666.bts.2.trx.1 format for trap prefix
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 CGI Request and use treq's routines to post it while setting up async handlers
"""
(ts, fx, lat, lon, height, opr, adm, pol, mcc, mnc) = data.split(',')
tstamp = datetime.datetime.fromtimestamp(float(ts)).isoformat()
self.factory.log.debug('location-state@%s.%s.%s.%s (%s) [%s/%s] => %s' % (net, bsc, bts, trx, tstamp, mcc, mnc, data))
params = {'bsc_id': bsc, 'lon': lon, 'lat': lat, 'position_validity': fix.get(fx, 0), 'time_stamp': tstamp, 'oper_status': oper.get(opr, 2), 'admin_status': admin.get(adm, 2), 'policy_status': policy.get(pol, 3) }
params['h'] = gen_hash(params, self.factory.secret_key)
d = post(self.factory.location, None, params=params)
d.addCallback(partial(handle_reply, 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" % (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): class TrapFactory(IPAFactory):
""" """
@ -165,21 +100,12 @@ class TrapFactory(IPAFactory):
self.log.setLevel(level) self.log.setLevel(level)
self.log.debug("Using IPA %s, CGI server: %s" % (Ctrl.version, self.location)) self.log.debug("Using IPA %s, CGI server: %s" % (Ctrl.version, self.location))
def prepare_params(bsc, lon, lat, fix, tstamp, oper, admin, policy):
def reloader(path, script, log, dbg1, dbg2, signum, _): params = {'bsc_id': bsc, 'lon': lon, 'lat': lat, 'position_validity': fix, 'time_stamp': tstamp, 'oper_status': oper, 'admin_status': admin, 'policy_status': policy }
""" params['h'] = gen_hash(params, self.factory.secret_key)
Signal handler: we have to use execl() because twisted's reactor is not restartable due to some bug in twisted implementation d = post(self.factory.location, None, params=params)
""" d.addCallback(partial(handle_reply, self.transport.write, self.factory.log))
log.info("Received Signal %d - restarting..." % signum) return d
if signum == signal.SIGUSR1 and dbg1 not in sys.argv and dbg2 not in sys.argv:
sys.argv.append(dbg1) # enforce debug
if signum == signal.SIGUSR2 and (dbg1 in sys.argv or dbg2 in sys.argv): # disable debug
if dbg1 in sys.argv:
sys.argv.remove(dbg1)
if dbg2 in sys.argv:
sys.argv.remove(dbg2)
os.execl(path, script, *sys.argv[1:])
if __name__ == '__main__': if __name__ == '__main__':
p = argparse.ArgumentParser(description='Proxy between given GCI service and Osmocom CTRL protocol.') p = argparse.ArgumentParser(description='Proxy between given GCI service and Osmocom CTRL protocol.')
@ -187,27 +113,14 @@ if __name__ == '__main__':
p.add_argument('-a', '--addr-ctrl', default='localhost', help="Adress to use for CTRL interface, defaults to localhost") p.add_argument('-a', '--addr-ctrl', default='localhost', help="Adress to use for CTRL interface, defaults to localhost")
p.add_argument('-p', '--port-ctrl', type=int, default=4250, help="Port to use for CTRL interface, defaults to 4250") p.add_argument('-p', '--port-ctrl', type=int, default=4250, help="Port to use for CTRL interface, defaults to 4250")
p.add_argument('-n', '--num-max-conn', type=int, default=5, help="Max number of concurrent HTTP requests to CGI server") p.add_argument('-n', '--num-max-conn', type=int, default=5, help="Max number of concurrent HTTP requests to CGI server")
p.add_argument('-d', '--debug', action='store_true', help="Enable debug log") p.add_argument('-d', '--debug', action='store_true', help="Enable debug log") # keep in sync with debug_init call below
p.add_argument('-o', '--output', action='store_true', help="Log to STDOUT in addition to SYSLOG") p.add_argument('-o', '--output', action='store_true', help="Log to STDOUT in addition to SYSLOG")
p.add_argument('-l', '--location', help="Location URL of the CGI server") p.add_argument('-l', '--location', help="Location URL of the CGI server")
p.add_argument('-s', '--secret-key', help="Secret key used to generate verification token") p.add_argument('-s', '--secret-key', help="Secret key used to generate verification token")
p.add_argument('-c', '--config-file', help="Path Config file. Cmd line args override values in config file") p.add_argument('-c', '--config-file', help="Path Config file. Cmd line args override values in config file")
args = p.parse_args() args = p.parse_args()
log = logging.getLogger('CTRL2CGI') log = debug_init('CTRL2CGI', args.debug, args.output)
if args.debug:
log.setLevel(logging.DEBUG)
else:
log.setLevel(logging.INFO)
log.addHandler(logging.handlers.SysLogHandler('/dev/log'))
if args.output:
log.addHandler(logging.StreamHandler(sys.stdout))
reboot = partial(reloader, os.path.abspath(__file__), os.path.basename(__file__), log, '-d', '--debug') # keep in sync with add_argument() call above
signal.signal(signal.SIGHUP, reboot)
signal.signal(signal.SIGQUIT, reboot)
signal.signal(signal.SIGUSR1, reboot) # restart and enabled debug output
signal.signal(signal.SIGUSR2, reboot) # restart and disable debug output
location_cfgfile = None location_cfgfile = None
secret_key_cfgfile = None secret_key_cfgfile = None

View File

@ -30,20 +30,13 @@ from osmopy.osmo_ipa import Ctrl
from treq import post, collect from treq import post, collect
from suds.client import Client from suds.client import Client
from functools import partial from functools import partial
from osmopy.trap_helper import Trap, reloader, debug_init
from distutils.version import StrictVersion as V # FIXME: use NormalizedVersion from PEP-386 when available from distutils.version import StrictVersion as V # FIXME: use NormalizedVersion from PEP-386 when available
import argparse, datetime, signal, sys, os, logging, logging.handlers import argparse, datetime, signal, sys, os, logging, logging.handlers
# we don't support older versions of TwistedIPA module # we don't support older versions of TwistedIPA module
assert V(twisted_ipa_version) > V('0.4') assert V(twisted_ipa_version) > V('0.4')
# keys from OpenBSC openbsc/src/libbsc/bsc_rf_ctrl.c, values SOAP-specific
oper = { 'inoperational' : 0, 'operational' : 1 }
admin = { 'locked' : 0, 'unlocked' : 1 }
policy = { 'off' : 0, 'on' : 1, 'grace' : 2, 'unknown' : 3 }
# keys from OpenBSC openbsc/src/libbsc/bsc_vty.c
fix = { 'invalid' : 0, 'fix2d' : 1, 'fix3d' : 1 } # SOAP server treats it as boolean but expects int
def handle_reply(p, f, log, r): def handle_reply(p, f, log, r):
""" """
@ -58,64 +51,6 @@ def handle_reply(p, f, log, r):
f(m) f(m)
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
"""
(l, r) = v.split()
loc = l.split('.')
t_type = loc[-1]
p = partial(lambda a, i: a[i] if len(a) > i else None, loc) # parse helper
method = getattr(self, 'handle_' + t_type.replace('-', ''), lambda: "Unhandled %s trap" % t_type)
method(p(1), p(3), p(5), p(7), r) # we expect net.0.bsc.666.bts.2.trx.1 format for trap prefix
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
"""
(ts, fx, lat, lon, height, opr, adm, pol, mcc, mnc) = data.split(',')
tstamp = datetime.datetime.fromtimestamp(float(ts)).isoformat()
self.factory.log.debug('location-state@%s.%s.%s.%s (%s) [%s/%s] => %s' % (net, bsc, bts, trx, tstamp, mcc, mnc, data))
ctx = self.factory.client.registerSiteLocation(bsc, float(lon), float(lat), fix.get(fx, 0), tstamp, oper.get(opr, 2), admin.get(adm, 2), policy.get(pol, 3))
d = post(self.factory.location, ctx.envelope)
d.addCallback(collect, partial(handle_reply, ctx.process_reply, 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" % (e, bsc)), 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): class TrapFactory(IPAFactory):
""" """
Store SOAP client object so TRAP handler can use it for requests Store SOAP client object so TRAP handler can use it for requests
@ -140,21 +75,12 @@ class TrapFactory(IPAFactory):
self.log.setLevel(level) self.log.setLevel(level)
self.log.debug("Using IPA %s, SUDS client: %s" % (Ctrl.version, soap)) self.log.debug("Using IPA %s, SUDS client: %s" % (Ctrl.version, soap))
def prepare_params(bsc, lon, lat, fix, tstamp, oper, admin, policy):
def reloader(path, script, log, dbg1, dbg2, signum, _): ctx = self.factory.client.registerSiteLocation(bsc, float(lon), float(lat), fix, tstamp, oper, admin, policy)
""" d = post(self.factory.location, ctx.envelope)
Signal handler: we have to use execl() because twisted's reactor is not restartable due to some bug in twisted implementation # treq's collect helper is handy to get all reply content at once using closure on ctx:
""" d.addCallback(collect, partial(handle_reply, ctx.process_reply, self.transport.write, self.factory.log))
log.info("Received Signal %d - restarting..." % signum) return d
if signum == signal.SIGUSR1 and dbg1 not in sys.argv and dbg2 not in sys.argv:
sys.argv.append(dbg1) # enforce debug
if signum == signal.SIGUSR2 and (dbg1 in sys.argv or dbg2 in sys.argv): # disable debug
if dbg1 in sys.argv:
sys.argv.remove(dbg1)
if dbg2 in sys.argv:
sys.argv.remove(dbg2)
os.execl(path, script, *sys.argv[1:])
if __name__ == '__main__': if __name__ == '__main__':
p = argparse.ArgumentParser(description='Proxy between given SOAP service and Osmocom CTRL protocol.') p = argparse.ArgumentParser(description='Proxy between given SOAP service and Osmocom CTRL protocol.')
@ -163,25 +89,12 @@ if __name__ == '__main__':
p.add_argument('-c', '--ctrl', default='localhost', help="Adress to use for CTRL interface, defaults to localhost") 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('-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('-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") p.add_argument('-d', '--debug', action='store_true', help="Enable debug log") # keep in sync with debug_init call below
p.add_argument('-o', '--output', action='store_true', help="Log to STDOUT in addition to SYSLOG") p.add_argument('-o', '--output', action='store_true', help="Log to STDOUT in addition to SYSLOG")
p.add_argument('-l', '--location', help="Override location found in WSDL file (don't use unless you know what you're doing)") 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() args = p.parse_args()
log = logging.getLogger('CTRL2SOAP') log = debug_init('CTRL2SOAP', args.debug, args.output)
if args.debug:
log.setLevel(logging.DEBUG)
else:
log.setLevel(logging.INFO)
log.addHandler(logging.handlers.SysLogHandler('/dev/log'))
if args.output:
log.addHandler(logging.StreamHandler(sys.stdout))
reboot = partial(reloader, os.path.abspath(__file__), os.path.basename(__file__), log, '-d', '--debug') # keep in sync with add_argument() call above
signal.signal(signal.SIGHUP, reboot)
signal.signal(signal.SIGQUIT, reboot)
signal.signal(signal.SIGUSR1, reboot) # restart and enabled debug output
signal.signal(signal.SIGUSR2, reboot) # restart and disable debug output
log.info("SOAP proxy %s starting with PID %d ..." % (__version__, os.getpid())) 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.connectTCP(args.ctrl, args.port, TrapFactory(args.ctrl, args.port, Trap, defer.DeferredSemaphore(args.num), log, args.wsdl, args.location))