def run_command_start(options): """ Subcommand "crossbar start". """ ## start Twisted logging ## log.startLogging(sys.stderr) log.msg("Crossbar.io {} node starting".format(crossbar.__version__)) ## we use an Autobahn utility to import the "best" available Twisted reactor ## reactor = install_reactor(options.reactor, options.debug) #if options.debug: # print("Running on reactor {}".format(reactor)) ## create and start Crossbar.io node ## node = Node(reactor, options.cbdir, options.debug) node.start() ## enter event loop ## reactor.run()
def main(): from autobahn.twisted.choosereactor import install_reactor reactor = install_reactor() observatory.start_logging(10) plugins = [ "sputnik.webserver.plugins.authz.default.DefaultPermissions", "sputnik.webserver.plugins.authn.anonymous.AnonymousLogin", "sputnik.webserver.plugins.authn.ip.IPFilter", "sputnik.webserver.plugins.authn.cookie.CookieLogin", "sputnik.webserver.plugins.authn.wampcra.WAMPCRALogin", "sputnik.webserver.plugins.authn.totp.TOTPVerification", "sputnik.webserver.plugins.db.mem.InMemoryDatabase", "sputnik.webserver.plugins.db.postgres.PostgresDatabase", "sputnik.webserver.plugins.backend.administrator.AdministratorProxy", "sputnik.webserver.plugins.backend.accountant.AccountantProxy", "sputnik.webserver.plugins.backend.cashier.CashierProxy", "sputnik.webserver.plugins.backend.alerts.AlertsProxy", "sputnik.webserver.plugins.rpc.registrar.RegistrarService", "sputnik.webserver.plugins.rpc.token.TokenService", "sputnik.webserver.plugins.rpc.info.InfoService", "sputnik.webserver.plugins.rpc.market.MarketService", "sputnik.webserver.plugins.rpc.private.PrivateService", "sputnik.webserver.plugins.rpc.trader.TraderService", "sputnik.webserver.plugins.feeds.market.MarketAnnouncer", "sputnik.webserver.plugins.feeds.user.UserAnnouncer", "sputnik.webserver.plugins.receiver.accountant.AccountantReceiver", "sputnik.webserver.plugins.receiver.engine.EngineReceiver", "sputnik.webserver.plugins.receiver.administrator.AdministratorReceiver", "sputnik.webserver.rest.RESTProxy" ] plugin.run_with_plugins(reactor, plugins, run, reactor)
def start_server(): logger.info('Running on reactor.\nInfo:wsurl=%s, tcpurl=%s', CONF.wsurl, CONF.websocket) reactor = choosereactor.install_reactor() wsfactory = WSServerFactory(CONF.wsurl) wsserver = endpoints.serverFromString(reactor, CONF.websocket) task.LoopingCall(ws.instructions.server.check_heartbeat, CONNECTS).start(15) task.LoopingCall(ws.instructions.common.ping_mysql).start(15) wsserver.listen(wsfactory) reactor.run()
def run_command_start(options): """ Subcommand "crossbar start". """ ## start Twisted logging ## if not options.logdir: logfd = sys.stderr else: from twisted.python.logfile import DailyLogFile logfd = DailyLogFile.fromFullPath( os.path.join(options.logdir, 'node.log')) from crossbar.twisted.processutil import DefaultSystemFileLogObserver flo = DefaultSystemFileLogObserver(logfd, system="{:<10} {:>6}".format( "Controller", os.getpid())) log.startLoggingWithObserver(flo.emit) log.msg("=" * 30 + " Crossbar.io " + "=" * 30 + "\n") import crossbar log.msg("Crossbar.io {} starting".format(crossbar.__version__)) ## we use an Autobahn utility to import the "best" available Twisted reactor ## reactor = install_reactor(options.reactor, options.debug) from twisted.python.reflect import qual log.msg("Running on {} using {} reactor".format( platform.python_implementation(), qual(reactor.__class__).split('.')[-1])) log.msg("Starting from node directory {}".format(options.cbdir)) ## create and start Crossbar.io node ## from crossbar.controller.node import Node node = Node(reactor, options) node.start() try: log.msg("Entering reactor event loop ...") reactor.run() except Exception as e: log.msg("Could not start reactor: {0}".format(e))
def start_client(): logger.info('start_client') host, port, key = ws.dbapi.blocking_get_sync_host_port_key() CLIENT['host'] = host CLIENT['port'] = port CLIENT['key'] = key wsurl = 'ws://%s:%s/ws/sync' % (host, port) reactor = choosereactor.install_reactor() logger.info('Running on reactor. Upstream:wsurl=%s', wsurl) wsfactory = WSClientFactory(wsurl) task.LoopingCall(ws.instructions.client.periodical_sync_data, CLIENT).start(settings.DEBUG and 15 or 180) # 3min task.LoopingCall(ws.instructions.client.periodical_sync_cache, CLIENT).start(15) task.LoopingCall(ws.instructions.client.periodical_sync_misc, CLIENT).start(settings.DEBUG and 15 or 1800) # 30min websocket.connectWS(wsfactory) reactor.run()
def run_command_start(options): """ Subcommand "crossbar start". """ ## start Twisted logging ## if not options.logdir: logfd = sys.stderr else: from twisted.python.logfile import DailyLogFile logfd = DailyLogFile.fromFullPath(os.path.join(options.logdir, 'node.log')) from crossbar.twisted.processutil import DefaultSystemFileLogObserver flo = DefaultSystemFileLogObserver(logfd, system = "{:<10} {:>6}".format("Controller", os.getpid())) log.startLoggingWithObserver(flo.emit) log.msg("=" * 30 + " Crossbar.io " + "=" * 30 + "\n") import crossbar log.msg("Crossbar.io {} starting".format(crossbar.__version__)) ## we use an Autobahn utility to import the "best" available Twisted reactor ## reactor = install_reactor(options.reactor, options.debug) from twisted.python.reflect import qual log.msg("Running on {} using {} reactor".format(platform.python_implementation(), qual(reactor.__class__).split('.')[-1])) log.msg("Starting from node directory {}".format(options.cbdir)) ## create and start Crossbar.io node ## from crossbar.controller.node import Node node = Node(reactor, options) node.start() try: log.msg("Entering reactor event loop ...") reactor.run() except Exception as e: log.msg("Could not start reactor: {0}".format(e))
def run_command_start(options): """ Subcommand "crossbar start". """ ## start Twisted logging ## if not options.logdir: logfd = sys.stderr else: from twisted.python.logfile import DailyLogFile logfd = DailyLogFile.fromFullPath(os.path.join(options.logdir, "node.log")) from crossbar.twisted.process import DefaultSystemFileLogObserver flo = DefaultSystemFileLogObserver(logfd, system="{:<10} {:>6}".format("Controller", os.getpid())) log.startLoggingWithObserver(flo.emit) log.msg("=" * 30 + " Crossbar.io " + "=" * 30 + "\n") log.msg("Crossbar.io {} node starting".format(crossbar.__version__)) ## we use an Autobahn utility to import the "best" available Twisted reactor ## reactor = install_reactor(options.reactor, options.debug) # if options.debug: # print("Running on reactor {}".format(reactor)) ## create and start Crossbar.io node ## node = Node(reactor, options) node.start() ## enter event loop ## reactor.run()
def __init__(self, debug = '0'): self.debug = debug log.startLogging(sys.stdout) ## we use an Autobahn utility to install the "best" available Twisted reactor ## from autobahn.twisted.choosereactor import install_reactor self.reactor = install_reactor() print("Running on reactor {}".format(self.reactor)) ## create a WAMP router factory ## from autobahn.twisted.wamp import RouterFactory self.router_factory = RouterFactory() ## create a WAMP router session factory ## from autobahn.twisted.wamp import RouterSessionFactory self.session_factory = RouterSessionFactory(self.router_factory) ## Add embedded WAMP application sessions to the router ## from core.rpc import MyBackendComponent component_config = types.ComponentConfig(realm = "realm1") component_session = MyBackendComponent(component_config) self.session_factory.add(component_session) ## create a WAMP-over-WebSocket transport server factory ## from autobahn.twisted.websocket import WampWebSocketServerFactory self.transport_factory = WampWebSocketServerFactory(self.session_factory, debug = self.debug) self.transport_factory.setProtocolOptions(failByDrop = False)
def run_command_version(options): """ Subcommand "crossbar version". """ reactor = install_reactor(options.reactor, options.debug) # Python # py_ver = '.'.join([str(x) for x in list(sys.version_info[:3])]) if options.debug: py_ver += " [%s]" % sys.version.replace('\n', ' ') if 'pypy_version_info' in sys.__dict__: py_ver_detail = "{}-{}".format( platform.python_implementation(), '.'.join(str(x) for x in sys.pypy_version_info[:3])) else: py_ver_detail = platform.python_implementation() # Twisted / Reactor # tx_ver = "%s-%s" % (pkg_resources.require("Twisted")[0].version, reactor.__class__.__name__) if options.debug: tx_ver += " [%s]" % qual(reactor.__class__) # Autobahn # from autobahn.websocket.protocol import WebSocketProtocol ab_ver = pkg_resources.require("autobahn")[0].version if options.debug: ab_ver += " [%s]" % qual(WebSocketProtocol) # UTF8 Validator # from autobahn.websocket.utf8validator import Utf8Validator s = qual(Utf8Validator) if 'wsaccel' in s: utf8_ver = 'wsaccel-%s' % pkg_resources.require('wsaccel')[0].version elif s.startswith('autobahn'): utf8_ver = 'autobahn' else: # could not detect UTF8 validator type/version utf8_ver = '?' if options.debug: utf8_ver += " [%s]" % qual(Utf8Validator) # XOR Masker # from autobahn.websocket.xormasker import XorMaskerNull s = qual(XorMaskerNull) if 'wsaccel' in s: xor_ver = 'wsaccel-%s' % pkg_resources.require('wsaccel')[0].version elif s.startswith('autobahn'): xor_ver = 'autobahn' else: # could not detect XOR masker type/version xor_ver = '?' if options.debug: xor_ver += " [%s]" % qual(XorMaskerNull) # JSON Serializer # from autobahn.wamp.serializer import JsonObjectSerializer s = str(JsonObjectSerializer.JSON_MODULE) if 'ujson' in s: json_ver = 'ujson-%s' % pkg_resources.require('ujson')[0].version else: json_ver = 'stdlib' # MsgPack Serializer # try: import msgpack # noqa msgpack_ver = 'msgpack-python-%s' % pkg_resources.require( 'msgpack-python')[0].version except ImportError: msgpack_ver = '-' for line in BANNER.splitlines(): print(click.style(("{:>40}").format(line), fg='yellow', bold=True)) print("Crossbar.io : {0}".format( click.style(crossbar.__version__, fg='yellow', bold=True))) print(" Autobahn : {0}".format( click.style(ab_ver, fg='yellow', bold=True))) print(" UTF8 Validator : {0}".format( click.style(utf8_ver, fg='yellow', bold=True))) print(" XOR Masker : {0}".format( click.style(xor_ver, fg='yellow', bold=True))) print(" JSON Codec : {0}".format( click.style(json_ver, fg='yellow', bold=True))) print(" MsgPack Codec : {0}".format( click.style(msgpack_ver, fg='yellow', bold=True))) print(" Twisted : {0}".format( click.style(tx_ver, fg='yellow', bold=True))) print(" Python : {0}/{1}".format( click.style(py_ver, fg='yellow', bold=True), click.style(py_ver_detail, fg='yellow', bold=True))) print("OS : {0}".format( click.style(platform.platform(), fg='yellow', bold=True))) print("Machine : {0}".format( click.style(platform.machine(), fg='yellow', bold=True))) print("")
def run(): """ Entry point into (native) worker processes. This wires up stuff such that a worker instance is talking WAMP-over-stdio to the node controller. """ import os import sys import platform # create the top-level parser # import argparse parser = argparse.ArgumentParser() parser.add_argument('-d', '--debug', action='store_true', help='Debug on (optional).') parser.add_argument('--reactor', default=None, choices=['select', 'poll', 'epoll', 'kqueue', 'iocp'], help='Explicit Twisted reactor selection (optional).') parser.add_argument('-c', '--cbdir', type=str, help="Crossbar.io node directory (required).") parser.add_argument('-n', '--node', type=str, help='Crossbar.io node ID (required).') parser.add_argument('-w', '--worker', type=str, help='Crossbar.io worker ID (required).') parser.add_argument('-r', '--realm', type=str, help='Crossbar.io node (management) realm (required).') parser.add_argument('-t', '--type', choices=['router', 'container'], help='Worker type (required).') parser.add_argument('--title', type=str, default=None, help='Worker process title to set (optional).') options = parser.parse_args() # make sure logging to something else than stdio is setup _first_ # from twisted.python import log from crossbar.twisted.processutil import BareFormatFileLogObserver flo = BareFormatFileLogObserver(sys.stderr) log.startLoggingWithObserver(flo.emit) try: import setproctitle except ImportError: log.msg("Warning: could not set worker process title (setproctitle not installed)") else: # set process title if requested to # if options.title: setproctitle.setproctitle(options.title) else: WORKER_TYPE_TO_TITLE = { 'router': 'crossbar-worker [router]', 'container': 'crossbar-worker [container]' } setproctitle.setproctitle(WORKER_TYPE_TO_TITLE[options.type].strip()) # we use an Autobahn utility to import the "best" available Twisted reactor # from autobahn.twisted.choosereactor import install_reactor reactor = install_reactor(options.reactor) from twisted.python.reflect import qual log.msg("Running under {} using {} reactor".format(platform.python_implementation(), qual(reactor.__class__).split('.')[-1])) options.cbdir = os.path.abspath(options.cbdir) os.chdir(options.cbdir) # log.msg("Starting from node directory {}".format(options.cbdir)) from crossbar.worker.router import RouterWorkerSession from crossbar.worker.container import ContainerWorkerSession WORKER_TYPE_TO_CLASS = { 'router': RouterWorkerSession, 'container': ContainerWorkerSession } from autobahn.twisted.websocket import WampWebSocketServerProtocol class WorkerServerProtocol(WampWebSocketServerProtocol): def connectionLost(self, reason): try: # this log message is unlikely to reach the controller (unless # only stdin/stdout pipes were lost, but not stderr) log.msg("Connection to node controller lost.") WampWebSocketServerProtocol.connectionLost(self, reason) except: pass finally: # loosing the connection to the node controller is fatal: # stop the reactor and exit with error if reactor.running: reactor.addSystemEventTrigger('after', 'shutdown', os._exit, 1) reactor.stop() else: sys.exit(1) try: # create a WAMP application session factory # from autobahn.twisted.wamp import ApplicationSessionFactory from autobahn.wamp.types import ComponentConfig session_config = ComponentConfig(realm=options.realm, extra=options) session_factory = ApplicationSessionFactory(session_config) session_factory.session = WORKER_TYPE_TO_CLASS[options.type] # create a WAMP-over-WebSocket transport server factory # from autobahn.twisted.websocket import WampWebSocketServerFactory transport_factory = WampWebSocketServerFactory(session_factory, "ws://localhost", debug=False, debug_wamp=False) transport_factory.protocol = WorkerServerProtocol transport_factory.setProtocolOptions(failByDrop=False) # create a protocol instance and wire up to stdio # from twisted.internet import stdio proto = transport_factory.buildProtocol(None) stdio.StandardIO(proto) # now start reactor loop # log.msg("Entering event loop ..") reactor.run() except Exception as e: log.msg("Unhandled exception: {}".format(e)) if reactor.running: reactor.addSystemEventTrigger('after', 'shutdown', os._exit, 1) reactor.stop() else: sys.exit(1)
def run_command_start(options): """ Subcommand "crossbar start". """ ## do not allow to run more than one Crossbar.io instance ## from the same Crossbar.io node directory ## pid_data = check_is_running(options.cbdir) if pid_data: print( "Crossbar.io is already running from node directory {} (PID {}).". format(options.cbdir, pid_data['pid'])) sys.exit(1) else: fp = os.path.join(options.cbdir, _PID_FILENAME) with open(fp, 'w') as fd: argv = options.argv options_dump = vars(options) del options_dump['func'] del options_dump['argv'] pid_data = { 'pid': os.getpid(), 'argv': argv, 'options': options_dump } fd.write("{}\n".format( json.dumps(pid_data, sort_keys=False, indent=3, separators=(',', ': ')))) ## we use an Autobahn utility to import the "best" available Twisted reactor ## reactor = install_reactor(options.reactor, options.debug) ## remove node PID file when reactor exits ## def remove_pid_file(): fp = os.path.join(options.cbdir, _PID_FILENAME) if os.path.isfile(fp): os.remove(fp) reactor.addSystemEventTrigger('after', 'shutdown', remove_pid_file) ## start Twisted logging ## if not options.logdir: logfd = sys.stderr else: from twisted.python.logfile import DailyLogFile logfd = DailyLogFile.fromFullPath( os.path.join(options.logdir, 'node.log')) from crossbar.twisted.processutil import DefaultSystemFileLogObserver flo = DefaultSystemFileLogObserver(logfd, system="{:<10} {:>6}".format( "Controller", os.getpid())) log.startLoggingWithObserver(flo.emit) log.msg("=" * 20 + " Crossbar.io " + "=" * 20 + "\n") import crossbar log.msg("Crossbar.io {} starting".format(crossbar.__version__)) from twisted.python.reflect import qual log.msg("Running on {} using {} reactor".format( platform.python_implementation(), qual(reactor.__class__).split('.')[-1])) log.msg("Starting from node directory {}".format(options.cbdir)) ## create and start Crossbar.io node ## from crossbar.controller.node import Node node = Node(reactor, options) node.start() try: log.msg("Entering reactor event loop ...") reactor.run() except Exception as e: log.msg("Could not start reactor: {0}".format(e))
def run(): """ Entry point into (native) worker processes. This wires up stuff such that a worker instance is talking WAMP-over-stdio to the node controller. """ import os import sys import platform import signal # Ignore SIGINT so we get consistent behavior on control-C versus # sending SIGINT to the controller process. When the controller is # shutting down, it sends TERM to all its children but ctrl-C # handling will send a SIGINT to all the processes in the group # (so then the controller sends a TERM but the child already or # will very shortly get a SIGINT as well). Twisted installs signal # handlers, but not for SIGINT if there's already a custom one # present. def ignore(sig, frame): log.debug("Ignoring SIGINT in worker.") signal.signal(signal.SIGINT, ignore) # create the top-level parser # import argparse parser = argparse.ArgumentParser() parser.add_argument('--reactor', default=None, choices=['select', 'poll', 'epoll', 'kqueue', 'iocp'], help='Explicit Twisted reactor selection (optional).') parser.add_argument('--loglevel', default="info", choices=['none', 'error', 'warn', 'info', 'debug', 'trace'], help='Initial log level.') parser.add_argument('-c', '--cbdir', type=str, help="Crossbar.io node directory (required).") parser.add_argument('-n', '--node', type=str, help='Crossbar.io node ID (required).') parser.add_argument('-w', '--worker', type=str, help='Crossbar.io worker ID (required).') parser.add_argument('-r', '--realm', type=str, help='Crossbar.io node (management) realm (required).') parser.add_argument('-t', '--type', choices=['router', 'container'], help='Worker type (required).') parser.add_argument('--title', type=str, default=None, help='Worker process title to set (optional).') options = parser.parse_args() # make sure logging to something else than stdio is setup _first_ # from crossbar._logging import make_JSON_observer, cb_logging_aware, _stderr from crossbar._logging import make_logger, log_publisher, start_logging from crossbar._logging import set_global_log_level # Set the global log level set_global_log_level(options.loglevel) log = make_logger() # Print a magic phrase that tells the capturing logger that it supports # Crossbar's rich logging print(cb_logging_aware, file=_stderr) _stderr.flush() flo = make_JSON_observer(_stderr) log_publisher.addObserver(flo) start_logging() try: import setproctitle except ImportError: log.debug("Could not set worker process title (setproctitle not installed)") else: # set process title if requested to # if options.title: setproctitle.setproctitle(options.title) else: WORKER_TYPE_TO_TITLE = { 'router': 'crossbar-worker [router]', 'container': 'crossbar-worker [container]' } setproctitle.setproctitle(WORKER_TYPE_TO_TITLE[options.type].strip()) # we use an Autobahn utility to import the "best" available Twisted reactor # from autobahn.twisted.choosereactor import install_reactor reactor = install_reactor(options.reactor) from twisted.python.reflect import qual log.info("Worker running under {python}-{reactor}", python=platform.python_implementation(), reactor=qual(reactor.__class__).split('.')[-1]) options.cbdir = os.path.abspath(options.cbdir) os.chdir(options.cbdir) # log.msg("Starting from node directory {}".format(options.cbdir)) from crossbar.worker.router import RouterWorkerSession from crossbar.worker.container import ContainerWorkerSession WORKER_TYPE_TO_CLASS = { 'router': RouterWorkerSession, 'container': ContainerWorkerSession } from autobahn.twisted.websocket import WampWebSocketServerProtocol class WorkerServerProtocol(WampWebSocketServerProtocol): def connectionLost(self, reason): try: # this log message is unlikely to reach the controller (unless # only stdin/stdout pipes were lost, but not stderr) log.warn("Connection to node controller lost.") WampWebSocketServerProtocol.connectionLost(self, reason) except: pass finally: # losing the connection to the node controller is fatal: # stop the reactor and exit with error log.info("No more controller connection; shutting down.") reactor.addSystemEventTrigger('after', 'shutdown', os._exit, 1) try: reactor.stop() except ReactorNotRunning: pass try: # create a WAMP application session factory # from autobahn.twisted.wamp import ApplicationSessionFactory from autobahn.wamp.types import ComponentConfig session_config = ComponentConfig(realm=options.realm, extra=options) session_factory = ApplicationSessionFactory(session_config) session_factory.session = WORKER_TYPE_TO_CLASS[options.type] # create a WAMP-over-WebSocket transport server factory # from autobahn.twisted.websocket import WampWebSocketServerFactory transport_factory = WampWebSocketServerFactory(session_factory, "ws://localhost", debug=False, debug_wamp=False) transport_factory.protocol = WorkerServerProtocol transport_factory.setProtocolOptions(failByDrop=False) # create a protocol instance and wire up to stdio # from twisted.python.runtime import platform as _platform from twisted.internet import stdio proto = transport_factory.buildProtocol(None) if _platform.isWindows(): stdio.StandardIO(proto) else: stdio.StandardIO(proto, stdout=3) # now start reactor loop # if False: log.info("vmprof enabled.") import os import vmprof PROFILE_FILE = 'vmprof_{}.dat'.format(os.getpid()) outfd = os.open(PROFILE_FILE, os.O_RDWR | os.O_CREAT | os.O_TRUNC) vmprof.enable(outfd, period=0.01) log.info("Entering event loop...") reactor.run() vmprof.disable() else: log.debug("Entering event loop...") reactor.run() except Exception as e: log.info("Unhandled exception: {}".format(e)) if reactor.running: reactor.addSystemEventTrigger('after', 'shutdown', os._exit, 1) reactor.stop() else: sys.exit(1)
except Exception: self.log.failure(None) else: self.log.error("Received NOTIFY on unknown channel {channel}", channel=notify.channel) if __name__ == '__main__': from autobahn.twisted.choosereactor import install_reactor from autobahn.twisted.wamp import ApplicationRunner import sys if sys.platform == 'win32': # IOCPReactor does did not implement addWriter: use select reactor install_reactor('select') else: install_reactor() config = { u'database': { u'host': u'127.0.0.1', u'port': 5432, u'port': u'$DBPORT', u'database': u'test', # u'user': u'$DBUSER', u'user': u'testuser', u'password': u'$DBPASSWORD' # u'password': u'testuser' } }
except Exception as e: print e else: print "unknown channel" if __name__ == '__main__': from autobahn.twisted.choosereactor import install_reactor from autobahn.twisted.wamp import ApplicationRunner import sys if sys.platform == 'win32': # IOCPReactor does did not implement addWriter: use select reactor install_reactor('select') else: install_reactor() config = { 'database': { 'host': u'127.0.0.1', 'port': 5432, 'port': u'$DBPORT', 'database': u'test', # 'user': u'$DBUSER', 'user': u'testuser', 'password': u'$DBPASSWORD' # 'password': u'testuser' } }
def run(prog=None, args=None, reactor=None): """ Entry point of Crossbar.io CLI. """ loglevel_args = { "type": str, "default": 'info', "choices": ['none', 'error', 'warn', 'info', 'debug', 'trace'], "help": ("How much Crossbar.io should log to the terminal, in order " "of verbosity.") } # create the top-level parser # parser = argparse.ArgumentParser(prog='crossbar', description="Crossbar.io - Polyglot application router - http://crossbar.io") # top-level options # parser.add_argument('--reactor', default=None, choices=['select', 'poll', 'epoll', 'kqueue', 'iocp'], help='Explicit Twisted reactor selection') # create subcommand parser # subparsers = parser.add_subparsers(dest='command', title='commands', help='Crossbar.io command to run') subparsers.required = True # "version" command # parser_version = subparsers.add_parser('version', help='Print software versions.') parser_version.add_argument('--loglevel', **loglevel_args) parser_version.set_defaults(func=run_command_version) # "init" command # parser_init = subparsers.add_parser('init', help='Initialize a new Crossbar.io node.') parser_init.set_defaults(func=run_command_init) parser_init.add_argument('--template', type=str, default='default', help="Template for initialization") parser_init.add_argument('--appdir', type=str, default=None, help="Application base directory where to create app and node from template.") # "templates" command # parser_templates = subparsers.add_parser('templates', help='List templates available for initializing a new Crossbar.io node.') parser_templates.set_defaults(func=run_command_templates) # "start" command # parser_start = subparsers.add_parser('start', help='Start a Crossbar.io node.') parser_start.set_defaults(func=run_command_start) parser_start.add_argument('--cbdir', type=str, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_start.add_argument('--config', type=str, default=None, help="Crossbar.io configuration file (overrides default CBDIR/config.json)") parser_start.add_argument('--logdir', type=str, default=None, help="Crossbar.io log directory (default: <Crossbar Node Directory>/)") parser_start.add_argument('--logtofile', action='store_true', help="Whether or not to log to file") parser_start.add_argument('--loglevel', **loglevel_args) parser_start.add_argument('--logformat', type=str, default='colour', choices=['syslogd', 'nocolour', 'colour'], help="The format of the logs -- suitable for syslogd, not coloured, or coloured.") # "stop" command # parser_stop = subparsers.add_parser('stop', help='Stop a Crossbar.io node.') parser_stop.add_argument('--cbdir', type=str, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_stop.set_defaults(func=run_command_stop) # "restart" command # parser_restart = subparsers.add_parser('restart', help='Restart a Crossbar.io node.') parser_restart.add_argument('--cbdir', type=str, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_restart.set_defaults(func=run_command_restart) # "status" command # parser_status = subparsers.add_parser('status', help='Checks whether a Crossbar.io node is running.') parser_status.add_argument('--cbdir', type=str, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_status.set_defaults(func=run_command_status) # "check" command # parser_check = subparsers.add_parser('check', help='Check a Crossbar.io node`s local configuration file.') parser_check.set_defaults(func=run_command_check) parser_check.add_argument('--cbdir', type=str, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_check.add_argument('--config', type=str, default=None, help="Crossbar.io configuration file (overrides default CBDIR/config.json)") # "convert" command # parser_check = subparsers.add_parser('convert', help='Convert a Crossbar.io node`s local configuration file from JSON to YAML or vice versa.') parser_check.set_defaults(func=run_command_convert) parser_check.add_argument('--cbdir', type=str, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_check.add_argument('--config', type=str, default=None, help="Crossbar.io configuration file (overrides default CBDIR/config.json)") # parse cmd line args # options = parser.parse_args(args) if args: options.argv = [prog] + args else: options.argv = sys.argv # Crossbar.io node directory # if hasattr(options, 'cbdir'): if not options.cbdir: if "CROSSBAR_DIR" in os.environ: options.cbdir = os.environ['CROSSBAR_DIR'] else: options.cbdir = '.crossbar' options.cbdir = os.path.abspath(options.cbdir) # Crossbar.io node configuration file # if hasattr(options, 'config'): if not options.config: for f in ['config.json', 'config.yaml']: f = os.path.join(options.cbdir, f) if os.path.isfile(f) and os.access(f, os.R_OK): options.config = f break if not options.config: raise Exception("No config file specified, and neither CBDIR/config.json nor CBDIR/config.yaml exists") else: options.config = os.path.join(options.cbdir, options.config) # Log directory # if hasattr(options, 'logdir'): if options.logdir: options.logdir = os.path.abspath(os.path.join(options.cbdir, options.logdir)) if not os.path.isdir(options.logdir): try: os.mkdir(options.logdir) except Exception as e: print("Could not create log directory: {}".format(e)) sys.exit(1) if not reactor: # try and get the log verboseness we want -- not all commands have a # loglevel, so just default to info in that case debug = getattr(options, "loglevel", "info") in ("debug", "trace") # we use an Autobahn utility to import the "best" available Twisted # reactor reactor = install_reactor(options.reactor, debug) # Start the logger _startlog(options, reactor) # run the subcommand selected # options.func(options, reactor=reactor)
def run(): """ Entry point into background worker process. This wires up stuff such that a WorkerProcess instance is talking WAMP over stdio to the node controller. """ ## create the top-level parser ## import argparse parser = argparse.ArgumentParser() parser.add_argument('-d', '--debug', action = 'store_true', help = 'Debug on.') parser.add_argument('--reactor', default = None, choices = ['select', 'poll', 'epoll', 'kqueue', 'iocp'], help = 'Explicit Twisted reactor selection') parser.add_argument('--cbdir', type = str, default = None, help = "Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser.add_argument('-l', '--logfile', default = None, help = 'Log to log file instead of stderr.') options = parser.parse_args() ## make sure logging to something else than stdio is setup _first_ ## if options.logfile: log.startLogging(open(options.logfile, 'a')) else: log.startLogging(sys.stderr) pid = os.getpid() ## Crossbar.io node directory ## if hasattr(options, 'cbdir') and not options.cbdir: if os.environ.has_key("CROSSBAR_DIR"): options.cbdir = os.environ['CROSSBAR_DIR'] else: options.cbdir = '.crossbar' options.cbdir = os.path.abspath(options.cbdir) ## we use an Autobahn utility to import the "best" available Twisted reactor ## from autobahn.twisted.choosereactor import install_reactor reactor = install_reactor(options.reactor) ## from twisted.python.reflect import qual log.msg("Worker {}: starting at node directory {} on {} ..".format(pid, options.cbdir, qual(reactor.__class__).split('.')[-1])) try: ## create a WAMP application session factory ## from autobahn.twisted.wamp import ApplicationSessionFactory session_factory = ApplicationSessionFactory() session_factory.options = options session_factory.session = WorkerProcess ## create a WAMP-over-WebSocket transport server factory ## from autobahn.twisted.websocket import WampWebSocketServerFactory transport_factory = WampWebSocketServerFactory(session_factory, "ws://localhost", debug = False) transport_factory.setProtocolOptions(failByDrop = False) ## create a protocol instance and wire up to stdio ## from twisted.internet import stdio proto = transport_factory.buildProtocol(None) stdio.StandardIO(proto) ## now start reactor loop ## log.msg("Worker {}: entering event loop ..".format(pid)) reactor.run() except Exception as e: log.msg("Worker {}: Unhandled exception - {}".format(pid, e)) raise e sys.exit(1)
def run(): import sys, argparse from twisted.python import log from twisted.internet.endpoints import serverFromString ## parse command line arguments ## def_wsocket = 'ws://127.0.0.1:8080/ws' def_xsocket = None def_user = '******' def_secret = '123test' def_realm = 'realm1' def_topic_base = 'sys' def_dsn = 'dbname=autobahn host=localhost user=autouser' def_endpoint = 'tcp:8080' def_engine = 'PG9_4' p = argparse.ArgumentParser( description="basicrouter example with database") p.add_argument('-w', '--websocket', action='store', dest='wsocket', default=def_wsocket, help='web socket ' + def_wsocket) p.add_argument('-x', '--xsocket', action='store', dest='xsocket', default=def_xsocket, help='x socket definition, default is: None ') p.add_argument('-r', '--realm', action='store', dest='realm', default=def_realm, help='connect to websocket using "realm", default ' + def_realm) p.add_argument('-v', '--verbose', action='store_true', dest='verbose', default=False, help='Verbose logging for debugging') p.add_argument('-u', '--user', action='store', dest='user', default=def_user, help='connect to websocket as user, default is: ' + def_user) p.add_argument('-s', '--secret', action='store', dest='password', default=def_secret, help='users "secret" password') p.add_argument('--debug', action='store_true', dest='debug', default=False, help='Autobahn layer debugging') p.add_argument( '-e', '--engine', action='store', dest='engine', default=def_engine, help= 'if specified, a database engine will be attached. Note engine is rooted on --topic' ) p.add_argument( "--endpoint", type=str, default="tcp:8080", help= 'Twisted server endpoint descriptor, e.g. "tcp:8080" or "unix:/tmp/mywebsocket", default is "' + def_endpoint + '"') p.add_argument( '-d', '--dsn', action='store', dest='dsn', default=def_dsn, help='if specified the database in dsn will be connected and ready') p.add_argument( '-t', '--topic', action='store', dest='topic_base', default=def_topic_base, help= 'if you specify --dsn then you will need a topic to root it on, the default ' + def_topic_base + ' is fine.') args = p.parse_args() if args.verbose: log.startLogging(sys.stdout) ## we use an Autobahn utility to install the "best" available Twisted reactor ## from autobahn.twisted.choosereactor import install_reactor reactor = install_reactor() log.msg("Running on reactor {}".format(reactor)) # database workers... userdb = UserDb(topic_base=args.topic_base + '.db', debug=args.verbose) sessiondb = SessionDb(topic_base=args.topic_base, debug=args.verbose) ## create a WAMP router factory ## component_config = types.ComponentConfig(realm=args.realm) xomponent_config = types.ComponentConfig(realm=args.realm) ai = { 'auth_type': 'wampcra', 'auth_user': args.user, 'auth_password': args.password } from autobahn.twisted.wamp import RouterFactory router_factory = RouterFactory() authorization_session = AuthorizeSession(component_config, topic_base=args.topic_base + '.db', debug=args.verbose, db=sessiondb, router=AuthorizeRouter) router_factory.router = authorization_session.ret_func ## create a WAMP router session factory ## from autobahn.twisted.wamp import RouterSessionFactory session_factory = RouterSessionFactory(router_factory) session_factory.session = MyRouterSession log.msg("session_factory.session") session_factory.userdb = userdb session_factory.sessiondb = sessiondb log.msg("userdb, sessiondb") sessiondb_component = SessionData(component_config, session_factory.sessiondb, topic_base=args.topic_base) session_factory.add(sessiondb_component) session_factory.add(authorization_session) log.msg("session_factory") db_session = DB(component_config, engine=args.engine, topic_base=args.topic_base + '.db', dsn=args.dsn, debug=args.verbose) session_factory.add(db_session) session_factory.userdb.set_session(db_session) session_factory.sessiondb.set_session(db_session) ## create a WAMP-over-WebSocket transport server factory ## from autobahn.twisted.websocket import WampWebSocketServerFactory transport_factory = WampWebSocketServerFactory(session_factory, debug=args.debug) transport_factory.setProtocolOptions(failByDrop=False) ## start the server from an endpoint ## ## this address clash detection was a goody I got from stackoverflow: ## http://stackoverflow.com/questions/12007316/exiting-twisted-application-after-listenfailure server = serverFromString(reactor, args.endpoint) def listen(): srv = server.listen(transport_factory) def ListenFailed(reason): log.msg("On Startup Listen Failed with {}".format(reason)) reactor.stop() srv.addErrback(ListenFailed) def addsession(): log.msg("here are three sessions {} {} {}".format( authorization_session, sessiondb_component, db_session)) qv = { "sessiondb_component": sessiondb_component._session_id, "db_session": db_session._session_id, "authorization_session": authorization_session._session_id } session_factory.sessiondb.set_system_sessions(qv) session_factory.sessiondb.add(0, sessiondb_component._session_id, sessiondb_component) session_factory.sessiondb.add(0, db_session._session_id, db_session) session_factory.sessiondb.add(0, authorization_session._session_id, authorization_session) reactor.callWhenRunning(listen) reactor.callWhenRunning(addsession) # if we set this router to be mastered by another, then start communicating with it if args.xsocket is not None: xdb = Xomponent(config=xomponent_config, authinfo=ai, topic_base=args.topic_base, debug=args.verbose, command='session', action='list', action_args={}) xdb.set_session(db_session) runner = ApplicationRunner(args.xsocket, args.realm) runner.run(lambda _: xdb, start_reactor=False) reactor.run()
def _run_command_exec_worker(options, reactor=None, personality=None): """ Entry point into (native) worker processes. This wires up stuff such that a worker instance is talking WAMP-over-stdio to the node controller. """ import os import sys import platform import signal # https://coverage.readthedocs.io/en/coverage-4.4.2/subprocess.html#measuring-sub-processes MEASURING_COVERAGE = False if 'COVERAGE_PROCESS_START' in os.environ: try: import coverage except ImportError: pass else: # The following will read the environment variable COVERAGE_PROCESS_START, # and that should be set to the .coveragerc file: # # export COVERAGE_PROCESS_START=${PWD}/.coveragerc # coverage.process_startup() MEASURING_COVERAGE = True # we use an Autobahn utility to import the "best" available Twisted reactor from autobahn.twisted.choosereactor import install_reactor reactor = install_reactor(options.reactor) # make sure logging to something else than stdio is setup _first_ from crossbar._logging import make_JSON_observer, cb_logging_aware from txaio import make_logger, start_logging from twisted.logger import globalLogPublisher from twisted.python.reflect import qual log = make_logger() # Print a magic phrase that tells the capturing logger that it supports # Crossbar's rich logging print(cb_logging_aware, file=sys.__stderr__) sys.__stderr__.flush() flo = make_JSON_observer(sys.__stderr__) globalLogPublisher.addObserver(flo) # Ignore SIGINT so we get consistent behavior on control-C versus # sending SIGINT to the controller process. When the controller is # shutting down, it sends TERM to all its children but ctrl-C # handling will send a SIGINT to all the processes in the group # (so then the controller sends a TERM but the child already or # will very shortly get a SIGINT as well). Twisted installs signal # handlers, but not for SIGINT if there's already a custom one # present. def ignore(sig, frame): log.debug("Ignoring SIGINT in worker.") signal.signal(signal.SIGINT, ignore) # actually begin logging start_logging(None, options.loglevel) # get personality klass, eg "crossbar.personality.Personality" l = options.personality.split('.') personality_module, personality_klass = '.'.join(l[:-1]), l[-1] # now load the personality module and class _mod = importlib.import_module(personality_module) Personality = getattr(_mod, personality_klass) # get worker klass, eg "crossbar.worker.container.ContainerController" l = options.klass.split('.') worker_module, worker_klass = '.'.join(l[:-1]), l[-1] # now load the worker module and class _mod = importlib.import_module(worker_module) klass = getattr(_mod, worker_klass) log.info( 'Starting worker "{worker_id}" for node "{node_id}" with personality "{personality}" {worker_class}', worker_id=options.worker, node_id=options.node, personality=Personality.NAME, worker_class=hltype(klass), ) log.info( 'Running as PID {pid} on {python}-{reactor}', pid=os.getpid(), python=platform.python_implementation(), reactor=qual(reactor.__class__).split('.')[-1], ) if MEASURING_COVERAGE: log.info(hl('Code coverage measurements enabled (coverage={coverage_version}).', color='green', bold=True), coverage_version=coverage.__version__) # set process title if requested to # try: import setproctitle except ImportError: log.debug("Could not set worker process title (setproctitle not installed)") else: if options.title: setproctitle.setproctitle(options.title) else: setproctitle.setproctitle('crossbar-worker [{}]'.format(options.klass)) # node directory # options.cbdir = os.path.abspath(options.cbdir) os.chdir(options.cbdir) # log.msg("Starting from node directory {}".format(options.cbdir)) # set process title if requested to # try: import setproctitle except ImportError: log.debug("Could not set worker process title (setproctitle not installed)") else: if options.title: setproctitle.setproctitle(options.title) else: setproctitle.setproctitle( 'crossbar-worker [{}]'.format(options.klass) ) from twisted.internet.error import ConnectionDone from autobahn.twisted.websocket import WampWebSocketServerProtocol class WorkerServerProtocol(WampWebSocketServerProtocol): def connectionLost(self, reason): # the behavior here differs slightly whether we're shutting down orderly # or shutting down because of "issues" if isinstance(reason.value, ConnectionDone): was_clean = True else: was_clean = False try: # this log message is unlikely to reach the controller (unless # only stdin/stdout pipes were lost, but not stderr) if was_clean: log.info("Connection to node controller closed cleanly") else: log.warn("Connection to node controller lost: {reason}", reason=reason) # give the WAMP transport a change to do it's thing WampWebSocketServerProtocol.connectionLost(self, reason) except: # we're in the process of shutting down .. so ignore .. pass finally: # after the connection to the node controller is gone, # the worker is "orphane", and should exit # determine process exit code if was_clean: exit_code = 0 else: exit_code = 1 # exit the whole worker process when the reactor has stopped reactor.addSystemEventTrigger('after', 'shutdown', os._exit, exit_code) # stop the reactor try: reactor.stop() except ReactorNotRunning: pass try: # define a WAMP application session factory # from autobahn.wamp.types import ComponentConfig def make_session(): session_config = ComponentConfig(realm=options.realm, extra=options) session = klass(config=session_config, reactor=reactor, personality=Personality) return session # create a WAMP-over-WebSocket transport server factory # from autobahn.twisted.websocket import WampWebSocketServerFactory transport_factory = WampWebSocketServerFactory(make_session, u'ws://localhost') transport_factory.protocol = WorkerServerProtocol transport_factory.setProtocolOptions(failByDrop=False) # create a protocol instance and wire up to stdio # from twisted.python.runtime import platform as _platform from twisted.internet import stdio proto = transport_factory.buildProtocol(None) if _platform.isWindows(): stdio.StandardIO(proto) else: stdio.StandardIO(proto, stdout=3) # now start reactor loop # if False: log.info("vmprof enabled.") import os import vmprof PROFILE_FILE = 'vmprof_{}.dat'.format(os.getpid()) outfd = os.open(PROFILE_FILE, os.O_RDWR | os.O_CREAT | os.O_TRUNC) vmprof.enable(outfd, period=0.01) log.info(hl('Entering event reactor ...', color='cyan', bold=True)) reactor.run() vmprof.disable() else: log.info(hl('Entering event reactor ...', color='cyan', bold=True)) reactor.run() except Exception as e: log.info("Unhandled exception: {e}", e=e) if reactor.running: reactor.addSystemEventTrigger('after', 'shutdown', os._exit, 1) reactor.stop() else: sys.exit(1)
def run(options, reactor=None): """ Entry point into (native) worker processes. This wires up stuff such that a worker instance is talking WAMP-over-stdio to the node controller. """ import os import sys import platform import signal # make sure logging to something else than stdio is setup _first_ # from crossbar._logging import make_JSON_observer, cb_logging_aware from txaio import make_logger, start_logging from twisted.logger import globalLogPublisher from twisted.python.reflect import qual log = make_logger() # Print a magic phrase that tells the capturing logger that it supports # Crossbar's rich logging print(cb_logging_aware, file=sys.__stderr__) sys.__stderr__.flush() flo = make_JSON_observer(sys.__stderr__) globalLogPublisher.addObserver(flo) # Ignore SIGINT so we get consistent behavior on control-C versus # sending SIGINT to the controller process. When the controller is # shutting down, it sends TERM to all its children but ctrl-C # handling will send a SIGINT to all the processes in the group # (so then the controller sends a TERM but the child already or # will very shortly get a SIGINT as well). Twisted installs signal # handlers, but not for SIGINT if there's already a custom one # present. def ignore(sig, frame): log.debug("Ignoring SIGINT in worker.") signal.signal(signal.SIGINT, ignore) # actually begin logging start_logging(None, options.loglevel) # we use an Autobahn utility to import the "best" available Twisted reactor # from autobahn.twisted.choosereactor import install_reactor reactor = install_reactor(options.reactor) # eg: crossbar.worker.container.ContainerWorkerSession l = options.klass.split('.') worker_module, worker_klass = '.'.join(l[:-1]), l[-1] # now load the worker module and class _mod = importlib.import_module(worker_module) klass = getattr(_mod, worker_klass) log.info( 'Started {worker_title} worker "{worker_id}" on node "{node_id}" [{klass} / {python}-{reactor}]', worker_title=klass.WORKER_TITLE, klass=options.klass, node_id=options.node, worker_id=options.worker, pid=os.getpid(), python=platform.python_implementation(), reactor=qual(reactor.__class__).split('.')[-1], ) # set process title if requested to # try: import setproctitle except ImportError: log.debug( "Could not set worker process title (setproctitle not installed)") else: if options.title: setproctitle.setproctitle(options.title) else: setproctitle.setproctitle('crossbar-worker [{}]'.format( options.klass)) # node directory # options.cbdir = os.path.abspath(options.cbdir) os.chdir(options.cbdir) # log.msg("Starting from node directory {}".format(options.cbdir)) # set process title if requested to # try: import setproctitle except ImportError: log.debug( "Could not set worker process title (setproctitle not installed)") else: if options.title: setproctitle.setproctitle(options.title) else: setproctitle.setproctitle('crossbar-worker [{}]'.format( options.klass)) from twisted.internet.error import ConnectionDone from autobahn.twisted.websocket import WampWebSocketServerProtocol class WorkerServerProtocol(WampWebSocketServerProtocol): def connectionLost(self, reason): # the behavior here differs slightly whether we're shutting down orderly # or shutting down because of "issues" if isinstance(reason.value, ConnectionDone): was_clean = True else: was_clean = False try: # this log message is unlikely to reach the controller (unless # only stdin/stdout pipes were lost, but not stderr) if was_clean: log.info("Connection to node controller closed cleanly") else: log.warn("Connection to node controller lost: {reason}", reason=reason) # give the WAMP transport a change to do it's thing WampWebSocketServerProtocol.connectionLost(self, reason) except: # we're in the process of shutting down .. so ignore .. pass finally: # after the connection to the node controller is gone, # the worker is "orphane", and should exit # determine process exit code if was_clean: exit_code = 0 else: exit_code = 1 # exit the whole worker process when the reactor has stopped reactor.addSystemEventTrigger('after', 'shutdown', os._exit, exit_code) # stop the reactor try: reactor.stop() except ReactorNotRunning: pass try: # create a WAMP application session factory # from autobahn.twisted.wamp import ApplicationSessionFactory from autobahn.wamp.types import ComponentConfig session_config = ComponentConfig(realm=options.realm, extra=options) session_factory = ApplicationSessionFactory(session_config) session_factory.session = klass # create a WAMP-over-WebSocket transport server factory # from autobahn.twisted.websocket import WampWebSocketServerFactory transport_factory = WampWebSocketServerFactory(session_factory, u'ws://localhost') transport_factory.protocol = WorkerServerProtocol transport_factory.setProtocolOptions(failByDrop=False) # create a protocol instance and wire up to stdio # from twisted.python.runtime import platform as _platform from twisted.internet import stdio proto = transport_factory.buildProtocol(None) if _platform.isWindows(): stdio.StandardIO(proto) else: stdio.StandardIO(proto, stdout=3) # now start reactor loop # if False: log.info("vmprof enabled.") import os import vmprof PROFILE_FILE = 'vmprof_{}.dat'.format(os.getpid()) outfd = os.open(PROFILE_FILE, os.O_RDWR | os.O_CREAT | os.O_TRUNC) vmprof.enable(outfd, period=0.01) log.info("Entering event loop...") reactor.run() vmprof.disable() else: log.debug("Entering event loop...") reactor.run() except Exception as e: log.info("Unhandled exception: {e}", e=e) if reactor.running: reactor.addSystemEventTrigger('after', 'shutdown', os._exit, 1) reactor.stop() else: sys.exit(1)
def run(): """ Entry point into (native) worker processes. This wires up stuff such that a worker instance is talking WAMP-over-stdio to the node controller. """ import os import sys import platform import signal # Ignore SIGINT so we get consistent behavior on control-C versus # sending SIGINT to the controller process. When the controller is # shutting down, it sends TERM to all its children but ctrl-C # handling will send a SIGINT to all the processes in the group # (so then the controller sends a TERM but the child already or # will very shortly get a SIGINT as well). Twisted installs signal # handlers, but not for SIGINT if there's already a custom one # present. def ignore(sig, frame): log.debug("Ignoring SIGINT in worker.") signal.signal(signal.SIGINT, ignore) # create the top-level parser # import argparse parser = argparse.ArgumentParser() parser.add_argument('--reactor', default=None, choices=['select', 'poll', 'epoll', 'kqueue', 'iocp'], help='Explicit Twisted reactor selection (optional).') parser.add_argument( '--loglevel', default="info", choices=['none', 'error', 'warn', 'info', 'debug', 'trace'], help='Initial log level.') parser.add_argument('-c', '--cbdir', type=six.text_type, help="Crossbar.io node directory (required).") parser.add_argument('-r', '--realm', type=six.text_type, help='Crossbar.io node (management) realm (required).') parser.add_argument('-t', '--type', choices=['router', 'container', 'websocket-testee'], help='Worker type (required).') parser.add_argument('-w', '--worker', type=six.text_type, help='Crossbar.io worker ID (required).') parser.add_argument('--title', type=six.text_type, default=None, help='Worker process title to set (optional).') parser.add_argument( '--expose_controller', type=bool, default=False, help= 'Expose node controller session to all components (this feature requires Crossbar.io Fabric extension).' ) parser.add_argument( '--expose_shared', type=bool, default=False, help= 'Expose a shared object to all components (this feature requires Crossbar.io Fabric extension).' ) options = parser.parse_args() # make sure logging to something else than stdio is setup _first_ # from crossbar._logging import make_JSON_observer, cb_logging_aware from txaio import make_logger, start_logging from twisted.logger import globalLogPublisher log = make_logger() # Print a magic phrase that tells the capturing logger that it supports # Crossbar's rich logging print(cb_logging_aware, file=sys.__stderr__) sys.__stderr__.flush() flo = make_JSON_observer(sys.__stderr__) globalLogPublisher.addObserver(flo) start_logging(None, options.loglevel) # we use an Autobahn utility to import the "best" available Twisted reactor # from autobahn.twisted.choosereactor import install_reactor reactor = install_reactor(options.reactor) WORKER_TYPE_TO_TITLE = { 'router': 'Router', 'container': 'Container', 'websocket-testee': 'WebSocket Testee' } from twisted.python.reflect import qual log.info( '{worker_title} worker "{worker_id}" process {pid} starting on {python}-{reactor} ..', worker_title=WORKER_TYPE_TO_TITLE[options.type], worker_id=options.worker, pid=os.getpid(), python=platform.python_implementation(), reactor=qual(reactor.__class__).split('.')[-1]) # set process title if requested to # try: import setproctitle except ImportError: log.debug( "Could not set worker process title (setproctitle not installed)") else: if options.title: setproctitle.setproctitle(options.title) else: WORKER_TYPE_TO_PROCESS_TITLE = { 'router': 'crossbar-worker [router]', 'container': 'crossbar-worker [container]', 'websocket-testee': 'crossbar-worker [websocket-testee]' } setproctitle.setproctitle( WORKER_TYPE_TO_PROCESS_TITLE[options.type].strip()) # node directory # options.cbdir = os.path.abspath(options.cbdir) os.chdir(options.cbdir) # log.msg("Starting from node directory {}".format(options.cbdir)) from crossbar.worker.router import RouterWorkerSession from crossbar.worker.container import ContainerWorkerSession from crossbar.worker.testee import WebSocketTesteeWorkerSession WORKER_TYPE_TO_CLASS = { 'router': RouterWorkerSession, 'container': ContainerWorkerSession, 'websocket-testee': WebSocketTesteeWorkerSession } from twisted.internet.error import ConnectionDone from autobahn.twisted.websocket import WampWebSocketServerProtocol class WorkerServerProtocol(WampWebSocketServerProtocol): def connectionLost(self, reason): # the behavior here differs slightly whether we're shutting down orderly # or shutting down because of "issues" if isinstance(reason.value, ConnectionDone): was_clean = True else: was_clean = False try: # this log message is unlikely to reach the controller (unless # only stdin/stdout pipes were lost, but not stderr) if was_clean: log.info("Connection to node controller closed cleanly") else: log.warn("Connection to node controller lost: {reason}", reason=reason) # give the WAMP transport a change to do it's thing WampWebSocketServerProtocol.connectionLost(self, reason) except: # we're in the process of shutting down .. so ignore .. pass finally: # after the connection to the node controller is gone, # the worker is "orphane", and should exit # determine process exit code if was_clean: exit_code = 0 else: exit_code = 1 # exit the whole worker process when the reactor has stopped reactor.addSystemEventTrigger('after', 'shutdown', os._exit, exit_code) # stop the reactor try: reactor.stop() except ReactorNotRunning: pass try: # create a WAMP application session factory # from autobahn.twisted.wamp import ApplicationSessionFactory from autobahn.wamp.types import ComponentConfig session_config = ComponentConfig(realm=options.realm, extra=options) session_factory = ApplicationSessionFactory(session_config) session_factory.session = WORKER_TYPE_TO_CLASS[options.type] # create a WAMP-over-WebSocket transport server factory # from autobahn.twisted.websocket import WampWebSocketServerFactory transport_factory = WampWebSocketServerFactory(session_factory, u'ws://localhost') transport_factory.protocol = WorkerServerProtocol transport_factory.setProtocolOptions(failByDrop=False) # create a protocol instance and wire up to stdio # from twisted.python.runtime import platform as _platform from twisted.internet import stdio proto = transport_factory.buildProtocol(None) if _platform.isWindows(): stdio.StandardIO(proto) else: stdio.StandardIO(proto, stdout=3) # now start reactor loop # if False: log.info("vmprof enabled.") import os import vmprof PROFILE_FILE = 'vmprof_{}.dat'.format(os.getpid()) outfd = os.open(PROFILE_FILE, os.O_RDWR | os.O_CREAT | os.O_TRUNC) vmprof.enable(outfd, period=0.01) log.info("Entering event loop...") reactor.run() vmprof.disable() else: log.debug("Entering event loop...") reactor.run() except Exception as e: log.info("Unhandled exception: {e}", e=e) if reactor.running: reactor.addSystemEventTrigger('after', 'shutdown', os._exit, 1) reactor.stop() else: sys.exit(1)
from autobahn.twisted.choosereactor import install_reactor install_reactor(verbose=True) import sys from twisted.internet import protocol, reactor, endpoints from twisted.python import log from twisted.internet.defer import inlineCallbacks, DeferredList from twisted.internet.task import LoopingCall from autobahn.util import rtime from autobahn.twisted.util import sleep from collections import deque log.startLogging(sys.stdout) import psutil p = psutil.Process() p.cpu_affinity([1]) print("CPU affinity set: {}".format(p.cpu_affinity())) size_histo = {} def print_histo(): print size_histo
def run(): import sys, argparse from twisted.python import log from twisted.internet.endpoints import serverFromString ## parse command line arguments ## def_wsocket = 'ws://127.0.0.1:8080/ws' def_realm = 'realm1' def_topic_base = 'sys' def_dsn = 'dbname=autobahn host=localhost user=autouser' def_endpoint='tcp:8080' def_engine = 'PG9_4' p = argparse.ArgumentParser(description="basicrouter example with database") p.add_argument('-w', '--websocket', action='store', dest='wsocket', default=def_wsocket, help='web socket '+def_wsocket) p.add_argument('-r', '--realm', action='store', dest='realm', default=def_realm, help='connect to websocket using "realm", default '+def_realm) p.add_argument('-v', '--verbose', action='store_true', dest='verbose', default=False, help='Verbose logging for debugging') p.add_argument('--debug', action='store_true', dest='debug', default=False, help='Autobahn layer debugging') p.add_argument('-e', '--engine', action='store', dest='engine', default=def_engine, help='if specified, a database engine will be attached. Note engine is rooted on --topic') p.add_argument("--endpoint", type = str, default = "tcp:8080", help = 'Twisted server endpoint descriptor, e.g. "tcp:8080" or "unix:/tmp/mywebsocket", default is "' + def_endpoint + '"') p.add_argument('-d', '--dsn', action='store', dest='dsn', default=def_dsn, help='if specified the database in dsn will be connected and ready') p.add_argument('-t', '--topic', action='store', dest='topic_base', default=def_topic_base, help='if you specify --dsn then you will need a topic to root it on, the default ' + def_topic_base + ' is fine.') args = p.parse_args() if args.verbose: log.startLogging(sys.stdout) ## we use an Autobahn utility to install the "best" available Twisted reactor ## from autobahn.twisted.choosereactor import install_reactor reactor = install_reactor() log.msg("Running on reactor {}".format(reactor)) # database workers... userdb = UserDb(topic_base=args.topic_base+'.db',debug=args.verbose) sessiondb = SessionDb(topic_base=args.topic_base,debug=args.verbose) ## create a WAMP router factory ## component_config = types.ComponentConfig(realm = args.realm) from autobahn.twisted.wamp import RouterFactory router_factory = RouterFactory() authorization_session = AuthorizeSession(component_config, topic_base=args.topic_base+'.db',debug=args.verbose,db=sessiondb,router=AuthorizeRouter) router_factory.router = authorization_session.ret_func ## create a WAMP router session factory ## from autobahn.twisted.wamp import RouterSessionFactory session_factory = RouterSessionFactory(router_factory) session_factory.session = MyRouterSession log.msg("session_factory.session") session_factory.userdb = userdb session_factory.sessiondb = sessiondb log.msg("userdb, sessiondb") sessiondb_component = SessionData(component_config,session_factory.sessiondb, topic_base=args.topic_base) session_factory.add(sessiondb_component) session_factory.add(authorization_session) log.msg("session_factory") db_session = DB(component_config, engine=args.engine, topic_base=args.topic_base+'.db', dsn=args.dsn, debug=args.verbose) session_factory.add(db_session) session_factory.userdb.set_session(db_session) session_factory.sessiondb.set_session(db_session) ## create a WAMP-over-WebSocket transport server factory ## from autobahn.twisted.websocket import WampWebSocketServerFactory transport_factory = WampWebSocketServerFactory(session_factory, debug = args.debug) transport_factory.setProtocolOptions(failByDrop = False) ## start the server from an endpoint ## ## this address clash detection was a goody I got from stackoverflow: ## http://stackoverflow.com/questions/12007316/exiting-twisted-application-after-listenfailure server = serverFromString(reactor, args.endpoint) def listen(): srv = server.listen(transport_factory) def ListenFailed(reason): log.msg("On Startup Listen Failed with {}".format(reason)) reactor.stop() srv.addErrback(ListenFailed) def addsession(): log.msg("here are three sessions {} {} {}".format(authorization_session, sessiondb_component, db_session)) qv = { "sessiondb_component":sessiondb_component._session_id, "db_session":db_session._session_id, "authorization_session":authorization_session._session_id } session_factory.sessiondb.set_system_sessions(qv) session_factory.sessiondb.add(0, sessiondb_component._session_id, sessiondb_component) session_factory.sessiondb.add(0, db_session._session_id, db_session) session_factory.sessiondb.add(0, authorization_session._session_id, authorization_session) reactor.callWhenRunning(listen) reactor.callWhenRunning(addsession) reactor.run()
def run(): """ Entry point into background worker process. This wires up stuff such that a WorkerProcess instance is talking WAMP over stdio to the node controller. """ ## create the top-level parser ## import argparse parser = argparse.ArgumentParser() parser.add_argument('-d', '--debug', action = 'store_true', help = 'Debug on.') parser.add_argument('--reactor', default = None, choices = ['select', 'poll', 'epoll', 'kqueue', 'iocp'], help = 'Explicit Twisted reactor selection') parser.add_argument('--cbdir', type = str, default = None, help = "Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser.add_argument('-n', '--name', default = None, help = 'Optional process name to set.') options = parser.parse_args() ## make sure logging to something else than stdio is setup _first_ ## from crossbar.twisted.process import BareFormatFileLogObserver flo = BareFormatFileLogObserver(sys.stderr) log.startLoggingWithObserver(flo.emit) ## the worker's PID ## pid = os.getpid() try: import setproctitle except ImportError: log.msg("Warning, could not set process title (setproctitle not installed)") else: ## set process title if requested to ## if options.name: setproctitle.setproctitle(options.name) else: setproctitle.setproctitle("Crossbar.io Worker") ## Crossbar.io node directory ## if hasattr(options, 'cbdir') and not options.cbdir: if os.environ.has_key("CROSSBAR_DIR"): options.cbdir = os.environ['CROSSBAR_DIR'] else: options.cbdir = '.crossbar' options.cbdir = os.path.abspath(options.cbdir) log.msg("Starting from node directory {}.".format(options.cbdir)) ## we use an Autobahn utility to import the "best" available Twisted reactor ## from autobahn.twisted.choosereactor import install_reactor reactor = install_reactor(options.reactor) from twisted.python.reflect import qual log.msg("Running on {} reactor.".format(qual(reactor.__class__).split('.')[-1])) from autobahn.twisted.websocket import WampWebSocketServerProtocol class WorkerServerProtocol(WampWebSocketServerProtocol): def connectionLost(self, reason): try: log.msg("Connection to node controller lost.") WampWebSocketServerProtocol.connectionLost(self, reason) except: pass finally: ## loosing the connection to the node controller (the pipes) is fatal. ## stop the reactor and exit with error if reactor.running: reactor.addSystemEventTrigger('after', 'shutdown', os._exit, 1) reactor.stop() else: sys.exit(1) try: ## create a WAMP application session factory ## from autobahn.twisted.wamp import ApplicationSessionFactory session_factory = ApplicationSessionFactory() session_factory.options = options session_factory.session = WorkerProcess ## create a WAMP-over-WebSocket transport server factory ## from autobahn.twisted.websocket import WampWebSocketServerFactory transport_factory = WampWebSocketServerFactory(session_factory, "ws://localhost", debug = False) transport_factory.protocol = WorkerServerProtocol transport_factory.setProtocolOptions(failByDrop = False) ## create a protocol instance and wire up to stdio ## from twisted.internet import stdio proto = transport_factory.buildProtocol(None) stdio.StandardIO(proto) ## now start reactor loop ## log.msg("Entering event loop ..") #reactor.callLater(4, reactor.stop) reactor.run() except Exception as e: log.msg("Unhandled exception - {}".format(e)) if reactor.running: reactor.addSystemEventTrigger('after', 'shutdown', os._exit, 1) reactor.stop() else: sys.exit(1)
raise Exception("notification payload type 'table' not implemented") else: raise Exception("logic error") except Exception as e: print e else: print "unknown channel" if __name__ == '__main__': from autobahn.twisted.choosereactor import install_reactor from autobahn.twisted.wamp import ApplicationRunner install_reactor() config = { 'database': { 'host': '127.0.0.1', 'port': 5432, 'database': 'test', 'user': '******', 'password': '******' } } runner = ApplicationRunner(url = "ws://127.0.0.1:8080/ws", realm = "realm1", extra = config) runner.run(PostgreSQLDatabasePublisher)
def run(): """ Entry point into (native) worker processes. This wires up stuff such that a worker instance is talking WAMP-over-stdio to the node controller. """ import os import sys import platform import signal # Ignore SIGINT so we get consistent behavior on control-C versus # sending SIGINT to the controller process. When the controller is # shutting down, it sends TERM to all its children but ctrl-C # handling will send a SIGINT to all the processes in the group # (so then the controller sends a TERM but the child already or # will very shortly get a SIGINT as well). Twisted installs signal # handlers, but not for SIGINT if there's already a custom one # present. def ignore(sig, frame): log.msg("Ignoring SIGINT in worker.") signal.signal(signal.SIGINT, ignore) # create the top-level parser # import argparse parser = argparse.ArgumentParser() parser.add_argument('-d', '--debug', action='store_true', help='Debug on (optional).') parser.add_argument('--reactor', default=None, choices=['select', 'poll', 'epoll', 'kqueue', 'iocp'], help='Explicit Twisted reactor selection (optional).') parser.add_argument('-c', '--cbdir', type=str, help="Crossbar.io node directory (required).") parser.add_argument('-n', '--node', type=str, help='Crossbar.io node ID (required).') parser.add_argument('-w', '--worker', type=str, help='Crossbar.io worker ID (required).') parser.add_argument('-r', '--realm', type=str, help='Crossbar.io node (management) realm (required).') parser.add_argument('-t', '--type', choices=['router', 'container'], help='Worker type (required).') parser.add_argument('--title', type=str, default=None, help='Worker process title to set (optional).') options = parser.parse_args() # make sure logging to something else than stdio is setup _first_ # from twisted.python import log from crossbar.twisted.processutil import BareFormatFileLogObserver flo = BareFormatFileLogObserver(sys.stderr) log.startLoggingWithObserver(flo.emit) try: import setproctitle except ImportError: log.msg( "Warning: could not set worker process title (setproctitle not installed)" ) else: # set process title if requested to # if options.title: setproctitle.setproctitle(options.title) else: WORKER_TYPE_TO_TITLE = { 'router': 'crossbar-worker [router]', 'container': 'crossbar-worker [container]' } setproctitle.setproctitle( WORKER_TYPE_TO_TITLE[options.type].strip()) # we use an Autobahn utility to import the "best" available Twisted reactor # from autobahn.twisted.choosereactor import install_reactor reactor = install_reactor(options.reactor) from twisted.python.reflect import qual log.msg("Running under {} using {} reactor".format( platform.python_implementation(), qual(reactor.__class__).split('.')[-1])) options.cbdir = os.path.abspath(options.cbdir) os.chdir(options.cbdir) # log.msg("Starting from node directory {}".format(options.cbdir)) from crossbar.worker.router import RouterWorkerSession from crossbar.worker.container import ContainerWorkerSession WORKER_TYPE_TO_CLASS = { 'router': RouterWorkerSession, 'container': ContainerWorkerSession } from autobahn.twisted.websocket import WampWebSocketServerProtocol class WorkerServerProtocol(WampWebSocketServerProtocol): def connectionLost(self, reason): try: # this log message is unlikely to reach the controller (unless # only stdin/stdout pipes were lost, but not stderr) log.msg("Connection to node controller lost.") WampWebSocketServerProtocol.connectionLost(self, reason) except: pass finally: # loosing the connection to the node controller is fatal: # stop the reactor and exit with error if reactor.running: reactor.addSystemEventTrigger('after', 'shutdown', os._exit, 1) reactor.stop() # if the reactor *isn't* running, we're already shutting down try: # create a WAMP application session factory # from autobahn.twisted.wamp import ApplicationSessionFactory from autobahn.wamp.types import ComponentConfig session_config = ComponentConfig(realm=options.realm, extra=options) session_factory = ApplicationSessionFactory(session_config) session_factory.session = WORKER_TYPE_TO_CLASS[options.type] # create a WAMP-over-WebSocket transport server factory # from autobahn.twisted.websocket import WampWebSocketServerFactory transport_factory = WampWebSocketServerFactory(session_factory, "ws://localhost", debug=False, debug_wamp=False) transport_factory.protocol = WorkerServerProtocol transport_factory.setProtocolOptions(failByDrop=False) # create a protocol instance and wire up to stdio # from twisted.internet import stdio proto = transport_factory.buildProtocol(None) stdio.StandardIO(proto) # now start reactor loop # log.msg("Entering event loop ..") reactor.run() except Exception as e: log.msg("Unhandled exception: {}".format(e)) if reactor.running: reactor.addSystemEventTrigger('after', 'shutdown', os._exit, 1) reactor.stop() else: sys.exit(1)
from autobahn.twisted.choosereactor import install_reactor install_reactor(verbose=True) import sys from twisted.internet import protocol, reactor, endpoints from twisted.python import log from twisted.internet.task import LoopingCall log.startLogging(sys.stdout) import psutil p = psutil.Process() p.cpu_affinity([0]) print("CPU affinity set: {}".format(p.cpu_affinity())) size_histo = {} def print_histo(): print size_histo lc = LoopingCall(print_histo) lc.start(1) class Echo(protocol.Protocol): def dataReceived(self, data): l = len(data) if l not in size_histo: size_histo[l] = 0
## You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## ############################################################################### import sys, argparse from autobahn.twisted.choosereactor import install_reactor install_reactor() from twisted.python import log from twisted.internet import reactor import autobahn from autobahn.twisted.websocket import listenWS from autobahn.wamp1.protocol import WampServerFactory, \ WampServerProtocol, \ WampCraServerProtocol class LoadLatencyBrokerProtocol(WampCraServerProtocol):
def _run_command_exec_worker(options, reactor=None, personality=None): """ Entry point into (native) worker processes. This wires up stuff such that a worker instance is talking WAMP-over-stdio to the node controller. """ import os import sys import platform import signal # https://coverage.readthedocs.io/en/coverage-4.4.2/subprocess.html#measuring-sub-processes MEASURING_COVERAGE = False if 'COVERAGE_PROCESS_START' in os.environ: try: import coverage except ImportError: pass else: # The following will read the environment variable COVERAGE_PROCESS_START, # and that should be set to the .coveragerc file: # # export COVERAGE_PROCESS_START=${PWD}/.coveragerc # coverage.process_startup() MEASURING_COVERAGE = True # we use an Autobahn utility to import the "best" available Twisted reactor from autobahn.twisted.choosereactor import install_reactor reactor = install_reactor(options.reactor) # make sure logging to something else than stdio is setup _first_ from crossbar._logging import make_JSON_observer, cb_logging_aware from txaio import make_logger, start_logging from twisted.logger import globalLogPublisher from twisted.python.reflect import qual log = make_logger() # Print a magic phrase that tells the capturing logger that it supports # Crossbar's rich logging print(cb_logging_aware, file=sys.__stderr__) sys.__stderr__.flush() flo = make_JSON_observer(sys.__stderr__) globalLogPublisher.addObserver(flo) term_print('CROSSBAR[{}]:WORKER_STARTING'.format(options.worker)) # Ignore SIGINT so we get consistent behavior on control-C versus # sending SIGINT to the controller process. When the controller is # shutting down, it sends TERM to all its children but ctrl-C # handling will send a SIGINT to all the processes in the group # (so then the controller sends a TERM but the child already or # will very shortly get a SIGINT as well). Twisted installs signal # handlers, but not for SIGINT if there's already a custom one # present. def ignore(sig, frame): log.debug("Ignoring SIGINT in worker.") signal.signal(signal.SIGINT, ignore) # actually begin logging start_logging(None, options.loglevel) # get personality klass, eg "crossbar.personality.Personality" l = options.personality.split('.') personality_module, personality_klass = '.'.join(l[:-1]), l[-1] # now load the personality module and class _mod = importlib.import_module(personality_module) Personality = getattr(_mod, personality_klass) # get worker klass, eg "crossbar.worker.container.ContainerController" l = options.klass.split('.') worker_module, worker_klass = '.'.join(l[:-1]), l[-1] # now load the worker module and class _mod = importlib.import_module(worker_module) klass = getattr(_mod, worker_klass) log.info( 'Starting {worker_type}-worker "{worker_id}" on node "{node_id}" (personality "{personality}") and local node management realm "{realm}" .. {worker_class}', worker_type=hl(klass.WORKER_TYPE), worker_id=hlid(options.worker), node_id=hlid(options.node), realm=hlid(options.realm), personality=hl(Personality.NAME), worker_class=hltype(klass), ) log.info( 'Running as PID {pid} on {python}-{reactor}', pid=os.getpid(), python=platform.python_implementation(), reactor=qual(reactor.__class__).split('.')[-1], ) if MEASURING_COVERAGE: log.info(hl( 'Code coverage measurements enabled (coverage={coverage_version}).', color='green', bold=True), coverage_version=coverage.__version__) # set process title if requested to # try: import setproctitle except ImportError: log.debug( "Could not set worker process title (setproctitle not installed)") else: if options.title: setproctitle.setproctitle(options.title) else: setproctitle.setproctitle('crossbar-worker [{}]'.format( options.klass)) # node directory # options.cbdir = os.path.abspath(options.cbdir) os.chdir(options.cbdir) # log.msg("Starting from node directory {}".format(options.cbdir)) # set process title if requested to # try: import setproctitle except ImportError: log.debug( "Could not set worker process title (setproctitle not installed)") else: if options.title: setproctitle.setproctitle(options.title) else: setproctitle.setproctitle('crossbar-worker [{}]'.format( options.klass)) from twisted.internet.error import ConnectionDone from autobahn.twisted.websocket import WampWebSocketServerProtocol class WorkerServerProtocol(WampWebSocketServerProtocol): def connectionLost(self, reason): # the behavior here differs slightly whether we're shutting down orderly # or shutting down because of "issues" if isinstance(reason.value, ConnectionDone): was_clean = True else: was_clean = False try: # this log message is unlikely to reach the controller (unless # only stdin/stdout pipes were lost, but not stderr) if was_clean: log.info("Connection to node controller closed cleanly") else: log.warn("Connection to node controller lost: {reason}", reason=reason) # give the WAMP transport a change to do it's thing WampWebSocketServerProtocol.connectionLost(self, reason) except: # we're in the process of shutting down .. so ignore .. pass finally: # after the connection to the node controller is gone, # the worker is "orphane", and should exit # determine process exit code if was_clean: exit_code = 0 else: exit_code = 1 # exit the whole worker process when the reactor has stopped reactor.addSystemEventTrigger('after', 'shutdown', os._exit, exit_code) # stop the reactor try: reactor.stop() except ReactorNotRunning: pass # if vmprof global profiling is enabled via command line option, this will carry # the file where vmprof writes its profile data if _HAS_VMPROF: _vm_prof = { # need to put this into a dict, since FDs are ints, and python closures can't # write to this otherwise 'outfd': None } # https://twistedmatrix.com/documents/current/api/twisted.internet.interfaces.IReactorCore.html # Each "system event" in Twisted, such as 'startup', 'shutdown', and 'persist', has 3 phases: # 'before', 'during', and 'after' (in that order, of course). These events will be fired # internally by the Reactor. def before_reactor_started(): term_print('CROSSBAR[{}]:REACTOR_STARTING'.format(options.worker)) def after_reactor_started(): term_print('CROSSBAR[{}]:REACTOR_STARTED'.format(options.worker)) if _HAS_VMPROF and options.vmprof: outfn = os.path.join( options.cbdir, '.vmprof-worker-{}-{}.dat'.format(options.worker, os.getpid())) _vm_prof['outfd'] = os.open(outfn, os.O_RDWR | os.O_CREAT | os.O_TRUNC) vmprof.enable(_vm_prof['outfd'], period=0.01) term_print('CROSSBAR[{}]:VMPROF_ENABLED:{}'.format( options.worker, outfn)) def before_reactor_stopped(): term_print('CROSSBAR[{}]:REACTOR_STOPPING'.format(options.worker)) if _HAS_VMPROF and options.vmprof and _vm_prof['outfd']: vmprof.disable() term_print('CROSSBAR[{}]:VMPROF_DISABLED'.format(options.worker)) def after_reactor_stopped(): # FIXME: we are indeed reaching this line, however, # the log output does not work (it also doesnt work using # plain old print). Dunno why. # my theory about this issue is: by the time this line # is reached, Twisted has already closed the stdout/stderr # pipes. hence we do an evil trick: we directly write to # the process' controlling terminal # https://unix.stackexchange.com/a/91716/52500 term_print('CROSSBAR[{}]:REACTOR_STOPPED'.format(options.worker)) reactor.addSystemEventTrigger('before', 'startup', before_reactor_started) reactor.addSystemEventTrigger('after', 'startup', after_reactor_started) reactor.addSystemEventTrigger('before', 'shutdown', before_reactor_stopped) reactor.addSystemEventTrigger('after', 'shutdown', after_reactor_stopped) try: # define a WAMP application session factory # from autobahn.wamp.types import ComponentConfig def make_session(): session_config = ComponentConfig(realm=options.realm, extra=options) session = klass(config=session_config, reactor=reactor, personality=Personality) return session # create a WAMP-over-WebSocket transport server factory # from autobahn.twisted.websocket import WampWebSocketServerFactory transport_factory = WampWebSocketServerFactory(make_session, 'ws://localhost') transport_factory.protocol = WorkerServerProtocol transport_factory.setProtocolOptions(failByDrop=False) # create a protocol instance and wire up to stdio # from twisted.python.runtime import platform as _platform from twisted.internet import stdio proto = transport_factory.buildProtocol(None) if _platform.isWindows(): stdio.StandardIO(proto) else: stdio.StandardIO(proto, stdout=3) # now start reactor loop # log.info(hl('Entering event reactor ...', color='green', bold=True)) reactor.run() except Exception as e: log.info("Unhandled exception: {e}", e=e) if reactor.running: reactor.addSystemEventTrigger('after', 'shutdown', os._exit, 1) reactor.stop() else: sys.exit(1)
def run_command_version(options): """ Subcommand "crossbar version". """ reactor = install_reactor(options.reactor, options.debug) ## Python ## py_ver = '.'.join([str(x) for x in list(sys.version_info[:3])]) if options.debug: py_ver += " [%s]" % sys.version.replace('\n', ' ') ## Twisted / Reactor ## tx_ver = "%s-%s" % (pkg_resources.require("Twisted")[0].version, reactor.__class__.__name__) if options.debug: tx_ver += " [%s]" % qual(reactor.__class__) ## Autobahn ## import autobahn from autobahn.websocket.protocol import WebSocketProtocol ab_ver = pkg_resources.require("autobahn")[0].version if options.debug: ab_ver += " [%s]" % qual(WebSocketProtocol) ## UTF8 Validator ## from autobahn.websocket.utf8validator import Utf8Validator s = str(Utf8Validator) if 'wsaccel' in s: utf8_ver = 'wsaccel-%s' % pkg_resources.require('wsaccel')[0].version elif s.startswith('autobahn'): utf8_ver = 'autobahn' else: raise Exception("could not detect UTF8 validator type/version") if options.debug: utf8_ver += " [%s]" % qual(Utf8Validator) ## XOR Masker ## from autobahn.websocket.xormasker import XorMaskerNull s = str(XorMaskerNull) if 'wsaccel' in s: xor_ver = 'wsaccel-%s' % pkg_resources.require('wsaccel')[0].version elif s.startswith('autobahn'): xor_ver = 'autobahn' else: raise Exception("could not detect XOR masker type/version") if options.debug: xor_ver += " [%s]" % qual(XorMaskerNull) import crossbar print("") print("Crossbar.io software versions:") print("") print("Crossbar.io : {0}".format(crossbar.__version__)) print("Autobahn : {0}".format(ab_ver)) print("Twisted : {0}".format(tx_ver)) print("Python : {0}".format(py_ver)) print("UTF8 Validator : {0}".format(utf8_ver)) print("XOR Masker : {0}".format(xor_ver)) print("")
## parser = argparse.ArgumentParser() parser.add_argument("-d", "--debug", action="store_true", help="Enable debug output.") parser.add_argument("--endpoint", type=str, default="tcp:8080", help='Twisted server endpoint descriptor, e.g. "tcp:8080" or "unix:/tmp/mywebsocket".') args = parser.parse_args() log.startLogging(sys.stdout) # we use an Autobahn utility to install the "best" available Twisted reactor ## from autobahn.twisted.choosereactor import install_reactor reactor = install_reactor() print("Running on reactor {}".format(reactor)) # create a WAMP router factory ## from autobahn.twisted.wamp import RouterFactory router_factory = RouterFactory() router_factory.router = MyRouter # create a WAMP router session factory ## from autobahn.twisted.wamp import RouterSessionFactory session_factory = RouterSessionFactory(router_factory) # create a WAMP-over-WebSocket transport server factory ##
def run_command_version(options): """ Subcommand "crossbar version". """ reactor = install_reactor(options.reactor, options.debug) ## Python ## py_ver = '.'.join([str(x) for x in list(sys.version_info[:3])]) if options.debug: py_ver += " [%s]" % sys.version.replace('\n', ' ') ## Twisted / Reactor ## tx_ver = "%s-%s" % (pkg_resources.require("Twisted")[0].version, reactor.__class__.__name__) if options.debug: tx_ver += " [%s]" % qual(reactor.__class__) ## Autobahn ## import autobahn from autobahn.websocket.protocol import WebSocketProtocol ab_ver = pkg_resources.require("autobahn")[0].version if options.debug: ab_ver += " [%s]" % qual(WebSocketProtocol) ## UTF8 Validator ## from autobahn.websocket.utf8validator import Utf8Validator s = str(Utf8Validator) if 'wsaccel' in s: utf8_ver = 'wsaccel-%s' % pkg_resources.require('wsaccel')[0].version elif s.startswith('autobahn'): utf8_ver = 'autobahn' else: # could not detect UTF8 validator type/version utf8_ver = '?' if options.debug: utf8_ver += " [%s]" % qual(Utf8Validator) ## XOR Masker ## from autobahn.websocket.xormasker import XorMaskerNull s = str(XorMaskerNull) if 'wsaccel' in s: xor_ver = 'wsaccel-%s' % pkg_resources.require('wsaccel')[0].version elif s.startswith('autobahn'): xor_ver = 'autobahn' else: # could not detect XOR masker type/version xor_ver = '?' if options.debug: xor_ver += " [%s]" % qual(XorMaskerNull) ## JSON Serializer ## from autobahn.wamp.serializer import JsonObjectSerializer s = str(JsonObjectSerializer.JSON_MODULE) if 'ujson' in s: json_ver = 'ujson-%s' % pkg_resources.require('ujson')[0].version else: json_ver = 'stdlib' ## MsgPack Serializer ## try: import msgpack msgpack_ver = 'msgpack-python-%s' % pkg_resources.require('msgpack-python')[0].version except ImportError: msgpack_ver = '-' import crossbar import platform print("") print("Crossbar.io package versions and platform information:") print("") print("Crossbar.io : {0}".format(crossbar.__version__)) print("") print(" Autobahn|Python : {0}".format(ab_ver)) print(" WebSocket UTF8 Validator : {0}".format(utf8_ver)) print(" WebSocket XOR Masker : {0}".format(xor_ver)) print(" WAMP JSON Codec : {0}".format(json_ver)) print(" WAMP MsgPack Codec : {0}".format(msgpack_ver)) print(" Twisted : {0}".format(tx_ver)) print(" Python : {0}-{1}".format(py_ver, platform.python_implementation())) print("") print("OS : {0}".format(platform.platform())) print("Machine : {0}".format(platform.machine())) print("")
def run(prog=None, args=None, reactor=None): """ Entry point of Crossbar.io CLI. """ loglevel_args = { "type": str, "default": 'info', "choices": ['none', 'error', 'warn', 'info', 'debug', 'trace'], "help": ("How much Crossbar.io should log to the terminal, in order " "of verbosity.") } colour_args = { "type": str, "default": "auto", "choices": ["true", "false", "auto"], "help": "If logging should be coloured." } # create the top-level parser # parser = argparse.ArgumentParser(prog='crossbar', description="Crossbar.io - https://crossbar.io") # top-level options # parser.add_argument('--reactor', default=None, choices=['select', 'poll', 'epoll', 'kqueue', 'iocp'], help='Explicit Twisted reactor selection') # create subcommand parser # subparsers = parser.add_subparsers(dest='command', title='commands', help='Crossbar.io command to run') subparsers.required = True # "version" command # parser_version = subparsers.add_parser('version', help='Print software versions.') parser_version.add_argument('--personality', type=six.text_type, default='community', choices=get_defined_personalities(), help=("Node personality to run.")) parser_version.add_argument('--loglevel', **loglevel_args) parser_version.add_argument('--colour', **colour_args) parser_version.set_defaults(func=run_command_version) # "keys" command # parser_keys = subparsers.add_parser('keys', help='Print Crossbar.io release and node keys.') parser_keys.add_argument('--cbdir', type=six.text_type, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_keys.add_argument('--loglevel', **loglevel_args) parser_keys.add_argument('--colour', **colour_args) parser_keys.set_defaults(func=run_command_keys) # "init" command # parser_init = subparsers.add_parser('init', help='Initialize a new Crossbar.io node.') parser_init.set_defaults(func=run_command_init) parser_init.add_argument('--template', type=six.text_type, default='default', help="Template for initialization") parser_init.add_argument('--appdir', type=six.text_type, default=None, help="Application base directory where to create app and node from template.") # Start a worker # parser_worker = subparsers.add_parser('start-worker', help='Start a worker process') parser_worker = process.get_argument_parser(parser_worker) parser_worker.set_defaults(func=process.run) # "templates" command # parser_templates = subparsers.add_parser('templates', help='List templates available for initializing a new Crossbar.io node.') parser_templates.set_defaults(func=run_command_templates) # "start" command # parser_start = subparsers.add_parser('start', help='Start a Crossbar.io node.') parser_start.set_defaults(func=run_command_start) parser_start.add_argument('--cbdir', type=six.text_type, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_start.add_argument('--config', type=six.text_type, default=None, help="Crossbar.io configuration file (overrides default CBDIR/config.json)") parser_start.add_argument('--personality', type=six.text_type, default='community', choices=get_defined_personalities(), help=("Node personality to run.")) parser_start.add_argument('--logdir', type=six.text_type, default=None, help="Crossbar.io log directory (default: <Crossbar Node Directory>/)") parser_start.add_argument('--logtofile', action='store_true', help="Whether or not to log to file") parser_start.add_argument('--loglevel', **loglevel_args) parser_start.add_argument('--colour', **colour_args) parser_start.add_argument('--logformat', type=six.text_type, default='standard', choices=['syslogd', 'standard', 'none'], help=("The format of the logs -- suitable for " "syslogd, not coloured, or coloured.")) # "stop" command # parser_stop = subparsers.add_parser('stop', help='Stop a Crossbar.io node.') parser_stop.add_argument('--cbdir', type=six.text_type, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_stop.set_defaults(func=run_command_stop) # "restart" command # parser_restart = subparsers.add_parser('restart', help='Restart a Crossbar.io node.') parser_restart.add_argument('--cbdir', type=six.text_type, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_restart.set_defaults(func=run_command_restart) # "status" command # parser_status = subparsers.add_parser('status', help='Checks whether a Crossbar.io node is running.') parser_status.add_argument('--cbdir', type=six.text_type, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_status.set_defaults(func=run_command_status) # "check" command # parser_check = subparsers.add_parser('check', help='Check a Crossbar.io node`s local configuration file.') parser_check.add_argument('--loglevel', **loglevel_args) parser_check.add_argument('--colour', **colour_args) parser_check.set_defaults(func=run_command_check) parser_check.add_argument('--cbdir', type=six.text_type, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_check.add_argument('--config', type=six.text_type, default=None, help="Crossbar.io configuration file (overrides default CBDIR/config.json)") # "convert" command # parser_check = subparsers.add_parser('convert', help='Convert a Crossbar.io node`s local configuration file from JSON to YAML or vice versa.') parser_check.set_defaults(func=run_command_convert) parser_check.add_argument('--cbdir', type=six.text_type, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_check.add_argument('--config', type=six.text_type, default=None, help="Crossbar.io configuration file (overrides default CBDIR/config.json)") # "upgrade" command # parser_check = subparsers.add_parser('upgrade', help='Upgrade a Crossbar.io node`s local configuration file to current configuration file format.') parser_check.set_defaults(func=run_command_upgrade) parser_check.add_argument('--cbdir', type=six.text_type, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_check.add_argument('--config', type=six.text_type, default=None, help="Crossbar.io configuration file (overrides default CBDIR/config.json)") # "keygen" command # help_text = 'Generate public/private keypairs for use with autobahn.wamp.cryptobox.KeyRing' parser_keygen = subparsers.add_parser( 'keygen', help=help_text, description=help_text, ) parser_keygen.set_defaults(func=run_command_keygen) # parse cmd line args # options = parser.parse_args(args) if args: options.argv = [prog] + args else: options.argv = sys.argv # coloured logging does not work on Windows, so overwrite it! # if sys.platform == 'win32': options.colour = False # IMPORTANT: keep the reactor install as early as possible to # avoid importing any Twisted module that comes with the side effect # of installing a default reactor (which might not be what we want!). if not reactor: # we use an Autobahn utility to import the "best" available Twisted reactor reactor = install_reactor(explicit_reactor=options.reactor, verbose=False, require_optimal_reactor=False) # ################## Twisted reactor installed FROM HERE ################## # Node personality # if hasattr(options, 'personality'): if options.personality not in get_installed_personalities(): print('FATAL: no Crossbar.io node personality "{}" installed'.format(options.personality)) sys.exit(1) # Crossbar.io node directory # if hasattr(options, 'cbdir'): if not options.cbdir: if "CROSSBAR_DIR" in os.environ: options.cbdir = os.environ['CROSSBAR_DIR'] elif os.path.isdir('.crossbar'): options.cbdir = '.crossbar' else: options.cbdir = '.' options.cbdir = os.path.abspath(options.cbdir) # convenience: if --cbdir points to a config file, take # the config file's base dirname as node directory if os.path.isfile(options.cbdir): options.cbdir = os.path.dirname(options.cbdir) # convenience: auto-create directory if not existing if not os.path.isdir(options.cbdir): try: os.mkdir(options.cbdir) except Exception as e: print("Could not create node directory: {}".format(e)) sys.exit(1) else: print("Auto-created node directory {}".format(options.cbdir)) # Crossbar.io node configuration file # if hasattr(options, 'config'): # if not explicit config filename is given, try to auto-detect . if not options.config: for f in ['config.yaml', 'config.json']: fn = os.path.join(options.cbdir, f) if os.path.isfile(fn) and os.access(fn, os.R_OK): options.config = f break # Log directory # if hasattr(options, 'logdir'): if options.logdir: options.logdir = os.path.abspath(os.path.join(options.cbdir, options.logdir)) if not os.path.isdir(options.logdir): try: os.mkdir(options.logdir) except Exception as e: print("Could not create log directory: {e}".format(e)) sys.exit(1) else: print("Auto-created log directory {}".format(options.logdir)) # Start the logger # _startlog(options, reactor) # run the subcommand selected # try: options.func(options, reactor=reactor) except SystemExit as e: # SystemExit(0) is okay! Anything other than that is bad and should be # re-raised. if e.args[0] != 0: raise
def run(prog=None, args=None, reactor=None): """ Entry point of Crossbar.io CLI. """ loglevel_args = { "type": str, "default": 'info', "choices": ['none', 'error', 'warn', 'info', 'debug', 'trace'], "help": ("How much Crossbar.io should log to the terminal, in order " "of verbosity.") } colour_args = { "type": str, "default": "auto", "choices": ["true", "false", "auto"], "help": "If logging should be coloured." } # create the top-level parser # parser = argparse.ArgumentParser(prog='crossbar', description="Crossbar.io - Polyglot application router - http://crossbar.io") # top-level options # parser.add_argument('--reactor', default=None, choices=['select', 'poll', 'epoll', 'kqueue', 'iocp'], help='Explicit Twisted reactor selection') # create subcommand parser # subparsers = parser.add_subparsers(dest='command', title='commands', help='Crossbar.io command to run') subparsers.required = True # "version" command # parser_version = subparsers.add_parser('version', help='Print software versions.') parser_version.add_argument('--loglevel', **loglevel_args) parser_version.add_argument('--colour', **colour_args) parser_version.set_defaults(func=run_command_version) # "init" command # parser_init = subparsers.add_parser('init', help='Initialize a new Crossbar.io node.') parser_init.set_defaults(func=run_command_init) parser_init.add_argument('--template', type=six.text_type, default='default', help="Template for initialization") parser_init.add_argument('--appdir', type=six.text_type, default=None, help="Application base directory where to create app and node from template.") # "templates" command # parser_templates = subparsers.add_parser('templates', help='List templates available for initializing a new Crossbar.io node.') parser_templates.set_defaults(func=run_command_templates) # "start" command # parser_start = subparsers.add_parser('start', help='Start a Crossbar.io node.') parser_start.set_defaults(func=run_command_start) parser_start.add_argument('--cdc', action='store_true', default=False, help='Start node in managed mode, connecting to Crossbar.io DevOps Center (CDC).') parser_start.add_argument('--cbdir', type=six.text_type, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_start.add_argument('--config', type=six.text_type, default=None, help="Crossbar.io configuration file (overrides default CBDIR/config.json)") parser_start.add_argument('--logdir', type=six.text_type, default=None, help="Crossbar.io log directory (default: <Crossbar Node Directory>/)") parser_start.add_argument('--logtofile', action='store_true', help="Whether or not to log to file") parser_start.add_argument('--loglevel', **loglevel_args) parser_start.add_argument('--colour', **colour_args) parser_start.add_argument('--logformat', type=six.text_type, default='standard', choices=['syslogd', 'standard', 'none'], help=("The format of the logs -- suitable for " "syslogd, not coloured, or coloured.")) # "stop" command # parser_stop = subparsers.add_parser('stop', help='Stop a Crossbar.io node.') parser_stop.add_argument('--cbdir', type=six.text_type, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_stop.set_defaults(func=run_command_stop) # "restart" command # parser_restart = subparsers.add_parser('restart', help='Restart a Crossbar.io node.') parser_restart.add_argument('--cbdir', type=six.text_type, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_restart.set_defaults(func=run_command_restart) # "status" command # parser_status = subparsers.add_parser('status', help='Checks whether a Crossbar.io node is running.') parser_status.add_argument('--cbdir', type=six.text_type, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_status.set_defaults(func=run_command_status) # "check" command # parser_check = subparsers.add_parser('check', help='Check a Crossbar.io node`s local configuration file.') parser_check.add_argument('--loglevel', **loglevel_args) parser_check.add_argument('--colour', **colour_args) parser_check.set_defaults(func=run_command_check) parser_check.add_argument('--cbdir', type=six.text_type, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_check.add_argument('--config', type=six.text_type, default=None, help="Crossbar.io configuration file (overrides default CBDIR/config.json)") # "convert" command # parser_check = subparsers.add_parser('convert', help='Convert a Crossbar.io node`s local configuration file from JSON to YAML or vice versa.') parser_check.set_defaults(func=run_command_convert) parser_check.add_argument('--cbdir', type=six.text_type, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_check.add_argument('--config', type=six.text_type, default=None, help="Crossbar.io configuration file (overrides default CBDIR/config.json)") # "upgrade" command # parser_check = subparsers.add_parser('upgrade', help='Upgrade a Crossbar.io node`s local configuration file to current configuration file format.') parser_check.set_defaults(func=run_command_upgrade) parser_check.add_argument('--cbdir', type=six.text_type, default=None, help="Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)") parser_check.add_argument('--config', type=six.text_type, default=None, help="Crossbar.io configuration file (overrides default CBDIR/config.json)") # parse cmd line args # options = parser.parse_args(args) if args: options.argv = [prog] + args else: options.argv = sys.argv # coloured logging does not work on Windows, so overwrite it! # if sys.platform == 'win32': options.colour = False # Crossbar.io node directory # if hasattr(options, 'cbdir'): if not options.cbdir: if "CROSSBAR_DIR" in os.environ: options.cbdir = os.environ['CROSSBAR_DIR'] elif os.path.isdir('.crossbar'): options.cbdir = '.crossbar' else: options.cbdir = '.' options.cbdir = os.path.abspath(options.cbdir) # convenience: if --cbdir points to a config file, take # the config file's base dirname as node directory if os.path.isfile(options.cbdir): options.cbdir = os.path.dirname(options.cbdir) # Crossbar.io node configuration file # if hasattr(options, 'config'): # if not explicit config filename is given, try to auto-detect . if not options.config: for f in ['config.json', 'config.yaml']: fn = os.path.join(options.cbdir, f) if os.path.isfile(fn) and os.access(fn, os.R_OK): options.config = f break if not options.config: if options.cdc: # in CDC mode, we will use a built-in default config # if not overridden from explicit config file pass else: raise Exception("No config file specified, and neither CBDIR/config.json nor CBDIR/config.yaml exists") # Log directory # if hasattr(options, 'logdir'): if options.logdir: options.logdir = os.path.abspath(os.path.join(options.cbdir, options.logdir)) if not os.path.isdir(options.logdir): try: os.mkdir(options.logdir) except Exception as e: print("Could not create log directory: {}".format(e)) sys.exit(1) if not reactor: # try and get the log verboseness we want -- not all commands have a # loglevel, so just default to info in that case debug = getattr(options, "loglevel", "info") in ("debug", "trace") # we use an Autobahn utility to import the "best" available Twisted # reactor reactor = install_reactor(options.reactor, debug) # Start the logger _startlog(options, reactor) # run the subcommand selected # try: options.func(options, reactor=reactor) except SystemExit as e: # SystemExit(0) is okay! Anything other than that is bad and should be # re-raised. if e.args[0] != 0: raise
def run_command_version(options): """ Subcommand "crossbar version". """ reactor = install_reactor(options.reactor, options.debug) ## Python ## py_ver = ".".join([str(x) for x in list(sys.version_info[:3])]) if options.debug: py_ver += " [%s]" % sys.version.replace("\n", " ") ## Twisted / Reactor ## tx_ver = "%s-%s" % (pkg_resources.require("Twisted")[0].version, reactor.__class__.__name__) if options.debug: tx_ver += " [%s]" % qual(reactor.__class__) ## Autobahn ## import autobahn from autobahn.websocket.protocol import WebSocketProtocol ab_ver = pkg_resources.require("autobahn")[0].version if options.debug: ab_ver += " [%s]" % qual(WebSocketProtocol) ## UTF8 Validator ## from autobahn.websocket.utf8validator import Utf8Validator s = str(Utf8Validator) if "wsaccel" in s: utf8_ver = "wsaccel-%s" % pkg_resources.require("wsaccel")[0].version elif s.startswith("autobahn"): utf8_ver = "autobahn" else: raise Exception("could not detect UTF8 validator type/version") if options.debug: utf8_ver += " [%s]" % qual(Utf8Validator) ## XOR Masker ## from autobahn.websocket.xormasker import XorMaskerNull s = str(XorMaskerNull) if "wsaccel" in s: xor_ver = "wsaccel-%s" % pkg_resources.require("wsaccel")[0].version elif s.startswith("autobahn"): xor_ver = "autobahn" else: raise Exception("could not detect XOR masker type/version") if options.debug: xor_ver += " [%s]" % qual(XorMaskerNull) import crossbar print print "Crossbar.io software versions:" print print "Crossbar.io : %s" % crossbar.__version__ print "Autobahn : %s" % ab_ver print "Twisted : %s" % tx_ver print "Python : %s" % py_ver print "UTF8 Validator : %s" % utf8_ver print "XOR Masker : %s" % xor_ver print
def run(prog=None, args=None, reactor=None): """ Entry point of Crossbar.io CLI. """ loglevel_args = { "type": str, "default": 'info', "choices": ['none', 'error', 'warn', 'info', 'debug', 'trace'], "help": ("How much Crossbar.io should log to the terminal, in order " "of verbosity.") } colour_args = { "type": str, "default": "auto", "choices": ["true", "false", "auto"], "help": "If logging should be coloured." } # create the top-level parser # parser = argparse.ArgumentParser( prog='crossbar', description= "Crossbar.io - Polyglot application router - http://crossbar.io") # top-level options # parser.add_argument('--reactor', default=None, choices=['select', 'poll', 'epoll', 'kqueue', 'iocp'], help='Explicit Twisted reactor selection') # create subcommand parser # subparsers = parser.add_subparsers(dest='command', title='commands', help='Crossbar.io command to run') subparsers.required = True # "version" command # parser_version = subparsers.add_parser('version', help='Print software versions.') parser_version.add_argument('--loglevel', **loglevel_args) parser_version.add_argument('--colour', **colour_args) parser_version.set_defaults(func=run_command_version) # "init" command # parser_init = subparsers.add_parser( 'init', help='Initialize a new Crossbar.io node.') parser_init.set_defaults(func=run_command_init) parser_init.add_argument('--template', type=six.text_type, default='default', help="Template for initialization") parser_init.add_argument( '--appdir', type=six.text_type, default=None, help= "Application base directory where to create app and node from template." ) # "templates" command # parser_templates = subparsers.add_parser( 'templates', help='List templates available for initializing a new Crossbar.io node.' ) parser_templates.set_defaults(func=run_command_templates) # "start" command # parser_start = subparsers.add_parser('start', help='Start a Crossbar.io node.') parser_start.set_defaults(func=run_command_start) parser_start.add_argument( '--cdc', action='store_true', default=False, help= 'Start node in managed mode, connecting to Crossbar.io DevOps Center (CDC).' ) parser_start.add_argument( '--cbdir', type=six.text_type, default=None, help= "Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)" ) parser_start.add_argument( '--config', type=six.text_type, default=None, help= "Crossbar.io configuration file (overrides default CBDIR/config.json)") parser_start.add_argument( '--logdir', type=six.text_type, default=None, help="Crossbar.io log directory (default: <Crossbar Node Directory>/)") parser_start.add_argument('--logtofile', action='store_true', help="Whether or not to log to file") parser_start.add_argument('--loglevel', **loglevel_args) parser_start.add_argument('--colour', **colour_args) parser_start.add_argument('--logformat', type=six.text_type, default='standard', choices=['syslogd', 'standard', 'none'], help=("The format of the logs -- suitable for " "syslogd, not coloured, or coloured.")) # "stop" command # parser_stop = subparsers.add_parser('stop', help='Stop a Crossbar.io node.') parser_stop.add_argument( '--cbdir', type=six.text_type, default=None, help= "Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)" ) parser_stop.set_defaults(func=run_command_stop) # "restart" command # parser_restart = subparsers.add_parser('restart', help='Restart a Crossbar.io node.') parser_restart.add_argument( '--cbdir', type=six.text_type, default=None, help= "Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)" ) parser_restart.set_defaults(func=run_command_restart) # "status" command # parser_status = subparsers.add_parser( 'status', help='Checks whether a Crossbar.io node is running.') parser_status.add_argument( '--cbdir', type=six.text_type, default=None, help= "Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)" ) parser_status.set_defaults(func=run_command_status) # "check" command # parser_check = subparsers.add_parser( 'check', help='Check a Crossbar.io node`s local configuration file.') parser_check.add_argument('--loglevel', **loglevel_args) parser_check.add_argument('--colour', **colour_args) parser_check.set_defaults(func=run_command_check) parser_check.add_argument( '--cbdir', type=six.text_type, default=None, help= "Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)" ) parser_check.add_argument( '--config', type=six.text_type, default=None, help= "Crossbar.io configuration file (overrides default CBDIR/config.json)") # "convert" command # parser_check = subparsers.add_parser( 'convert', help= 'Convert a Crossbar.io node`s local configuration file from JSON to YAML or vice versa.' ) parser_check.set_defaults(func=run_command_convert) parser_check.add_argument( '--cbdir', type=six.text_type, default=None, help= "Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)" ) parser_check.add_argument( '--config', type=six.text_type, default=None, help= "Crossbar.io configuration file (overrides default CBDIR/config.json)") # "upgrade" command # parser_check = subparsers.add_parser( 'upgrade', help= 'Upgrade a Crossbar.io node`s local configuration file to current configuration file format.' ) parser_check.set_defaults(func=run_command_upgrade) parser_check.add_argument( '--cbdir', type=six.text_type, default=None, help= "Crossbar.io node directory (overrides ${CROSSBAR_DIR} and the default ./.crossbar)" ) parser_check.add_argument( '--config', type=six.text_type, default=None, help= "Crossbar.io configuration file (overrides default CBDIR/config.json)") # parse cmd line args # options = parser.parse_args(args) if args: options.argv = [prog] + args else: options.argv = sys.argv # coloured logging does not work on Windows, so overwrite it! # if sys.platform == 'win32': options.colour = False # Crossbar.io node directory # if hasattr(options, 'cbdir'): if not options.cbdir: if "CROSSBAR_DIR" in os.environ: options.cbdir = os.environ['CROSSBAR_DIR'] elif os.path.isdir('.crossbar'): options.cbdir = '.crossbar' else: options.cbdir = '.' options.cbdir = os.path.abspath(options.cbdir) # Crossbar.io node configuration file # if hasattr(options, 'config'): # if not explicit config filename is given, try to auto-detect . if not options.config: for f in ['config.json', 'config.yaml']: fn = os.path.join(options.cbdir, f) if os.path.isfile(fn) and os.access(fn, os.R_OK): options.config = f break if not options.config: if options.cdc: # in CDC mode, we will use a built-in default config # if not overridden from explicit config file pass else: raise Exception( "No config file specified, and neither CBDIR/config.json nor CBDIR/config.yaml exists" ) # Log directory # if hasattr(options, 'logdir'): if options.logdir: options.logdir = os.path.abspath( os.path.join(options.cbdir, options.logdir)) if not os.path.isdir(options.logdir): try: os.mkdir(options.logdir) except Exception as e: print("Could not create log directory: {}".format(e)) sys.exit(1) if not reactor: # try and get the log verboseness we want -- not all commands have a # loglevel, so just default to info in that case debug = getattr(options, "loglevel", "info") in ("debug", "trace") # we use an Autobahn utility to import the "best" available Twisted # reactor reactor = install_reactor(options.reactor, debug) # Start the logger _startlog(options, reactor) # run the subcommand selected # try: options.func(options, reactor=reactor) except SystemExit as e: # SystemExit(0) is okay! Anything other than that is bad and should be # re-raised. if e.args[0] != 0: raise
def run_command_start(options): """ Subcommand "crossbar start". """ # do not allow to run more than one Crossbar.io instance # from the same Crossbar.io node directory # pid_data = check_is_running(options.cbdir) if pid_data: print("Crossbar.io is already running from node directory {} (PID {}).".format(options.cbdir, pid_data['pid'])) sys.exit(1) else: fp = os.path.join(options.cbdir, _PID_FILENAME) with open(fp, 'w') as fd: argv = options.argv options_dump = vars(options) del options_dump['func'] del options_dump['argv'] pid_data = { 'pid': os.getpid(), 'argv': argv, 'options': options_dump } fd.write("{}\n".format(json.dumps(pid_data, sort_keys=False, indent=3, separators=(',', ': ')))) # we use an Autobahn utility to import the "best" available Twisted reactor # reactor = install_reactor(options.reactor, options.debug) # remove node PID file when reactor exits # def remove_pid_file(): fp = os.path.join(options.cbdir, _PID_FILENAME) if os.path.isfile(fp): os.remove(fp) reactor.addSystemEventTrigger('after', 'shutdown', remove_pid_file) # start Twisted logging # if not options.logdir: logfd = sys.stderr else: from twisted.python.logfile import DailyLogFile logfd = DailyLogFile.fromFullPath(os.path.join(options.logdir, 'node.log')) from crossbar.twisted.processutil import DefaultSystemFileLogObserver flo = DefaultSystemFileLogObserver(logfd, system="{:<10} {:>6}".format("Controller", os.getpid())) log.startLoggingWithObserver(flo.emit) log.msg("=" * 20 + " Crossbar.io " + "=" * 20 + "\n") import crossbar log.msg("Crossbar.io {} starting".format(crossbar.__version__)) from twisted.python.reflect import qual log.msg("Running on {} using {} reactor".format(platform.python_implementation(), qual(reactor.__class__).split('.')[-1])) log.msg("Starting from node directory {}".format(options.cbdir)) # create and start Crossbar.io node # from crossbar.controller.node import Node node = Node(reactor, options) node.start() try: log.msg("Entering reactor event loop ...") reactor.run() except Exception as e: log.msg("Could not start reactor: {0}".format(e))
def run_command_start(options): """ Subcommand "crossbar start". """ # do not allow to run more than one Crossbar.io instance # from the same Crossbar.io node directory # pid_data = check_is_running(options.cbdir) if pid_data: print( "Crossbar.io is already running from node directory {} (PID {}).". format(options.cbdir, pid_data['pid'])) sys.exit(1) else: fp = os.path.join(options.cbdir, _PID_FILENAME) with open(fp, 'w') as fd: argv = options.argv options_dump = vars(options) del options_dump['func'] del options_dump['argv'] pid_data = { 'pid': os.getpid(), 'argv': argv, 'options': options_dump } fd.write("{}\n".format( json.dumps(pid_data, sort_keys=False, indent=3, separators=(',', ': ')))) # we use an Autobahn utility to import the "best" available Twisted reactor # reactor = install_reactor(options.reactor, options.debug) # remove node PID file when reactor exits # def remove_pid_file(): fp = os.path.join(options.cbdir, _PID_FILENAME) if os.path.isfile(fp): os.remove(fp) reactor.addSystemEventTrigger('after', 'shutdown', remove_pid_file) # start Twisted logging # from crossbar._logging import log_publisher, make_logger from crossbar._logging import start_logging, set_global_log_level set_global_log_level(options.loglevel) log = make_logger() if options.logdir: # We want to log to a file from crossbar._logging import make_legacy_daily_logfile_observer log_publisher.addObserver( make_legacy_daily_logfile_observer(options.logdir, options.loglevel)) else: # We want to log to stdout/stderr. from crossbar._logging import make_stdout_observer from crossbar._logging import make_stderr_observer if options.loglevel == "none": # Do no logging! pass elif options.loglevel == "error": # Error: Only print errors to stderr. log_publisher.addObserver( make_stdout_observer(show_source=False, format=options.logformat)) log_publisher.addObserver( make_stderr_observer(show_source=False, format=options.logformat)) elif options.loglevel == "warn": # Print warnings+ to stderr. log_publisher.addObserver( make_stdout_observer(show_source=False, format=options.logformat)) log_publisher.addObserver( make_stderr_observer(show_source=False, format=options.logformat)) elif options.loglevel == "info": # Print info to stdout, warn+ to stderr log_publisher.addObserver( make_stdout_observer(show_source=False, format=options.logformat)) log_publisher.addObserver( make_stderr_observer(show_source=False, format=options.logformat)) elif options.loglevel == "debug": # Print debug+info to stdout, warn+ to stderr, with the class # source log_publisher.addObserver( make_stdout_observer(show_source=True, format=options.logformat)) log_publisher.addObserver( make_stderr_observer(show_source=True, format=options.logformat)) elif options.loglevel == "trace": # Print trace+, with the class source log_publisher.addObserver( make_stdout_observer(show_source=True, format=options.logformat, trace=True)) log_publisher.addObserver( make_stderr_observer(show_source=True, format=options.logformat)) else: assert False, "Shouldn't ever get here." # Actually start the logger. start_logging() for line in BANNER.splitlines(): log.info(click.style(("{:>40}").format(line), fg='yellow', bold=True)) bannerFormat = "{:>12} {:<24}" log.info( bannerFormat.format( "Version:", click.style(crossbar.__version__, fg='yellow', bold=True))) # log.info(bannerFormat.format("Python:", click.style(platform.python_implementation(), fg='yellow', bold=True))) # log.info(bannerFormat.format("Reactor:", click.style(qual(reactor.__class__).split('.')[-1], fg='yellow', bold=True))) log.info( bannerFormat.format("Started:", click.style(utcnow(), fg='yellow', bold=True))) log.info() log.info("Starting from node directory {}".format(options.cbdir)) # create and start Crossbar.io node # def start_crossbar(): from crossbar.controller.node import Node node = Node(reactor, options) d = node.start() def on_error(err): log.error("Could not start node: {error}", error=err.value) if reactor.running: reactor.stop() d.addErrback(on_error) reactor.callWhenRunning(start_crossbar) try: log.info("Entering reactor event loop...") reactor.run() except Exception: log.failure("Could not start reactor: {log_failure.value}")
def run(): """ Entry point into (native) worker processes. This wires up stuff such that a worker instance is talking WAMP-over-stdio to the node controller. """ import os import sys import platform import signal # Ignore SIGINT so we get consistent behavior on control-C versus # sending SIGINT to the controller process. When the controller is # shutting down, it sends TERM to all its children but ctrl-C # handling will send a SIGINT to all the processes in the group # (so then the controller sends a TERM but the child already or # will very shortly get a SIGINT as well). Twisted installs signal # handlers, but not for SIGINT if there's already a custom one # present. def ignore(sig, frame): log.debug("Ignoring SIGINT in worker.") signal.signal(signal.SIGINT, ignore) # create the top-level parser # import argparse parser = argparse.ArgumentParser() parser.add_argument('-d', '--debug', action='store_true', help='Debug on (optional).') parser.add_argument('--reactor', default=None, choices=['select', 'poll', 'epoll', 'kqueue', 'iocp'], help='Explicit Twisted reactor selection (optional).') parser.add_argument('-c', '--cbdir', type=str, help="Crossbar.io node directory (required).") parser.add_argument('-n', '--node', type=str, help='Crossbar.io node ID (required).') parser.add_argument('-w', '--worker', type=str, help='Crossbar.io worker ID (required).') parser.add_argument('-r', '--realm', type=str, help='Crossbar.io node (management) realm (required).') parser.add_argument('-t', '--type', choices=['router', 'container'], help='Worker type (required).') parser.add_argument('--title', type=str, default=None, help='Worker process title to set (optional).') options = parser.parse_args() # make sure logging to something else than stdio is setup _first_ # from twisted.logger import globalLogBeginner from crossbar._logging import Logger, make_JSON_observer log = Logger() _stderr = sys.stderr flo = make_JSON_observer(_stderr) globalLogBeginner.beginLoggingTo([flo]) try: import setproctitle except ImportError: log.info("Warning: could not set worker process title (setproctitle not installed)") else: # set process title if requested to # if options.title: setproctitle.setproctitle(options.title) else: WORKER_TYPE_TO_TITLE = { 'router': 'crossbar-worker [router]', 'container': 'crossbar-worker [container]' } setproctitle.setproctitle(WORKER_TYPE_TO_TITLE[options.type].strip()) # we use an Autobahn utility to import the "best" available Twisted reactor # from autobahn.twisted.choosereactor import install_reactor reactor = install_reactor(options.reactor) from twisted.python.reflect import qual log.info("Running under {python} using {reactor} reactor", python=platform.python_implementation(), reactor=qual(reactor.__class__).split('.')[-1]) options.cbdir = os.path.abspath(options.cbdir) os.chdir(options.cbdir) # log.msg("Starting from node directory {}".format(options.cbdir)) from crossbar.worker.router import RouterWorkerSession from crossbar.worker.container import ContainerWorkerSession WORKER_TYPE_TO_CLASS = { 'router': RouterWorkerSession, 'container': ContainerWorkerSession } from autobahn.twisted.websocket import WampWebSocketServerProtocol class WorkerServerProtocol(WampWebSocketServerProtocol): def connectionLost(self, reason): try: # this log message is unlikely to reach the controller (unless # only stdin/stdout pipes were lost, but not stderr) log.warn("Connection to node controller lost.") WampWebSocketServerProtocol.connectionLost(self, reason) except: pass finally: # loosing the connection to the node controller is fatal: # stop the reactor and exit with error if reactor.running: reactor.addSystemEventTrigger('after', 'shutdown', os._exit, 1) reactor.stop() # if the reactor *isn't* running, we're already shutting down try: # create a WAMP application session factory # from autobahn.twisted.wamp import ApplicationSessionFactory from autobahn.wamp.types import ComponentConfig session_config = ComponentConfig(realm=options.realm, extra=options) session_factory = ApplicationSessionFactory(session_config) session_factory.session = WORKER_TYPE_TO_CLASS[options.type] # create a WAMP-over-WebSocket transport server factory # from autobahn.twisted.websocket import WampWebSocketServerFactory transport_factory = WampWebSocketServerFactory(session_factory, "ws://localhost", debug=False, debug_wamp=False) transport_factory.protocol = WorkerServerProtocol transport_factory.setProtocolOptions(failByDrop=False) # create a protocol instance and wire up to stdio # from twisted.internet import stdio proto = transport_factory.buildProtocol(None) stdio.StandardIO(proto) # now start reactor loop # log.info("Entering event loop...") reactor.run() except Exception as e: log.info("Unhandled exception: {}".format(e)) if reactor.running: reactor.addSystemEventTrigger('after', 'shutdown', os._exit, 1) reactor.stop() else: sys.exit(1)
def _run_command_exec_worker(options, reactor=None, personality=None): """ Entry point into (native) worker processes. This wires up stuff such that a worker instance is talking WAMP-over-stdio to the node controller. """ import os import sys import platform import signal # https://coverage.readthedocs.io/en/coverage-4.4.2/subprocess.html#measuring-sub-processes MEASURING_COVERAGE = False if 'COVERAGE_PROCESS_START' in os.environ: try: import coverage except ImportError: pass else: # The following will read the environment variable COVERAGE_PROCESS_START, # and that should be set to the .coveragerc file: # # export COVERAGE_PROCESS_START=${PWD}/.coveragerc # coverage.process_startup() MEASURING_COVERAGE = True # we use an Autobahn utility to import the "best" available Twisted reactor from autobahn.twisted.choosereactor import install_reactor reactor = install_reactor(options.reactor) # make sure logging to something else than stdio is setup _first_ from crossbar._logging import make_JSON_observer, cb_logging_aware from txaio import make_logger, start_logging from twisted.logger import globalLogPublisher from twisted.python.reflect import qual log = make_logger() # Print a magic phrase that tells the capturing logger that it supports # Crossbar's rich logging print(cb_logging_aware, file=sys.__stderr__) sys.__stderr__.flush() flo = make_JSON_observer(sys.__stderr__) globalLogPublisher.addObserver(flo) # Ignore SIGINT so we get consistent behavior on control-C versus # sending SIGINT to the controller process. When the controller is # shutting down, it sends TERM to all its children but ctrl-C # handling will send a SIGINT to all the processes in the group # (so then the controller sends a TERM but the child already or # will very shortly get a SIGINT as well). Twisted installs signal # handlers, but not for SIGINT if there's already a custom one # present. def ignore(sig, frame): log.debug("Ignoring SIGINT in worker.") signal.signal(signal.SIGINT, ignore) # actually begin logging start_logging(None, options.loglevel) # now check if we can import the requested personality Personality = crossbar.personalities().get(options.personality, None) if not Personality: raise Exception('logic error: personality "{}" not installed'.format(options.personality)) # eg: crossbar.worker.container.ContainerController l = options.klass.split('.') worker_module, worker_klass = '.'.join(l[:-1]), l[-1] # now load the worker module and class _mod = importlib.import_module(worker_module) klass = getattr(_mod, worker_klass) log.info( 'Starting worker "{worker_id}" for node "{node_id}" with personality "{personality}" {worker_class}', worker_id=options.worker, node_id=options.node, personality=Personality.NAME, worker_class=hltype(klass), ) log.info( 'Running as PID {pid} on {python}-{reactor}', pid=os.getpid(), python=platform.python_implementation(), reactor=qual(reactor.__class__).split('.')[-1], ) if MEASURING_COVERAGE: log.info(hl('Code coverage measurements enabled (coverage={coverage_version}).', color='green', bold=True), coverage_version=coverage.__version__) # set process title if requested to # try: import setproctitle except ImportError: log.debug("Could not set worker process title (setproctitle not installed)") else: if options.title: setproctitle.setproctitle(options.title) else: setproctitle.setproctitle('crossbar-worker [{}]'.format(options.klass)) # node directory # options.cbdir = os.path.abspath(options.cbdir) os.chdir(options.cbdir) # log.msg("Starting from node directory {}".format(options.cbdir)) # set process title if requested to # try: import setproctitle except ImportError: log.debug("Could not set worker process title (setproctitle not installed)") else: if options.title: setproctitle.setproctitle(options.title) else: setproctitle.setproctitle( 'crossbar-worker [{}]'.format(options.klass) ) from twisted.internet.error import ConnectionDone from autobahn.twisted.websocket import WampWebSocketServerProtocol class WorkerServerProtocol(WampWebSocketServerProtocol): def connectionLost(self, reason): # the behavior here differs slightly whether we're shutting down orderly # or shutting down because of "issues" if isinstance(reason.value, ConnectionDone): was_clean = True else: was_clean = False try: # this log message is unlikely to reach the controller (unless # only stdin/stdout pipes were lost, but not stderr) if was_clean: log.info("Connection to node controller closed cleanly") else: log.warn("Connection to node controller lost: {reason}", reason=reason) # give the WAMP transport a change to do it's thing WampWebSocketServerProtocol.connectionLost(self, reason) except: # we're in the process of shutting down .. so ignore .. pass finally: # after the connection to the node controller is gone, # the worker is "orphane", and should exit # determine process exit code if was_clean: exit_code = 0 else: exit_code = 1 # exit the whole worker process when the reactor has stopped reactor.addSystemEventTrigger('after', 'shutdown', os._exit, exit_code) # stop the reactor try: reactor.stop() except ReactorNotRunning: pass try: # define a WAMP application session factory # from autobahn.wamp.types import ComponentConfig def make_session(): session_config = ComponentConfig(realm=options.realm, extra=options) session = klass(config=session_config, reactor=reactor, personality=Personality) return session # create a WAMP-over-WebSocket transport server factory # from autobahn.twisted.websocket import WampWebSocketServerFactory transport_factory = WampWebSocketServerFactory(make_session, u'ws://localhost') transport_factory.protocol = WorkerServerProtocol transport_factory.setProtocolOptions(failByDrop=False) # create a protocol instance and wire up to stdio # from twisted.python.runtime import platform as _platform from twisted.internet import stdio proto = transport_factory.buildProtocol(None) if _platform.isWindows(): stdio.StandardIO(proto) else: stdio.StandardIO(proto, stdout=3) # now start reactor loop # if False: log.info("vmprof enabled.") import os import vmprof PROFILE_FILE = 'vmprof_{}.dat'.format(os.getpid()) outfd = os.open(PROFILE_FILE, os.O_RDWR | os.O_CREAT | os.O_TRUNC) vmprof.enable(outfd, period=0.01) log.info(hl('Entering event reactor ...', color='cyan', bold=True)) reactor.run() vmprof.disable() else: log.info(hl('Entering event reactor ...', color='cyan', bold=True)) reactor.run() except Exception as e: log.info("Unhandled exception: {e}", e=e) if reactor.running: reactor.addSystemEventTrigger('after', 'shutdown', os._exit, 1) reactor.stop() else: sys.exit(1)
def run_command_version(options): """ Subcommand "crossbar version". """ reactor = install_reactor(options.reactor, options.debug) # Python # py_ver = '.'.join([str(x) for x in list(sys.version_info[:3])]) if options.debug: py_ver += " [%s]" % sys.version.replace('\n', ' ') if 'pypy_version_info' in sys.__dict__: py_ver_detail = "{}-{}".format(platform.python_implementation(), '.'.join(str(x) for x in sys.pypy_version_info[:3])) else: py_ver_detail = platform.python_implementation() # Twisted / Reactor # tx_ver = "%s-%s" % (pkg_resources.require("Twisted")[0].version, reactor.__class__.__name__) if options.debug: tx_ver += " [%s]" % qual(reactor.__class__) # Autobahn # from autobahn.websocket.protocol import WebSocketProtocol ab_ver = pkg_resources.require("autobahn")[0].version if options.debug: ab_ver += " [%s]" % qual(WebSocketProtocol) # UTF8 Validator # from autobahn.websocket.utf8validator import Utf8Validator s = qual(Utf8Validator) if 'wsaccel' in s: utf8_ver = 'wsaccel-%s' % pkg_resources.require('wsaccel')[0].version elif s.startswith('autobahn'): utf8_ver = 'autobahn' else: # could not detect UTF8 validator type/version utf8_ver = '?' if options.debug: utf8_ver += " [%s]" % qual(Utf8Validator) # XOR Masker # from autobahn.websocket.xormasker import XorMaskerNull s = qual(XorMaskerNull) if 'wsaccel' in s: xor_ver = 'wsaccel-%s' % pkg_resources.require('wsaccel')[0].version elif s.startswith('autobahn'): xor_ver = 'autobahn' else: # could not detect XOR masker type/version xor_ver = '?' if options.debug: xor_ver += " [%s]" % qual(XorMaskerNull) # JSON Serializer # from autobahn.wamp.serializer import JsonObjectSerializer s = str(JsonObjectSerializer.JSON_MODULE) if 'ujson' in s: json_ver = 'ujson-%s' % pkg_resources.require('ujson')[0].version else: json_ver = 'stdlib' # MsgPack Serializer # try: import msgpack # noqa msgpack_ver = 'msgpack-python-%s' % pkg_resources.require('msgpack-python')[0].version except ImportError: msgpack_ver = '-' for line in BANNER.splitlines(): print(click.style(("{:>40}").format(line), fg='yellow', bold=True)) print("Crossbar.io : {0}".format(click.style(crossbar.__version__, fg='yellow', bold=True))) print(" Autobahn : {0}".format(click.style(ab_ver, fg='yellow', bold=True))) print(" UTF8 Validator : {0}".format(click.style(utf8_ver, fg='yellow', bold=True))) print(" XOR Masker : {0}".format(click.style(xor_ver, fg='yellow', bold=True))) print(" JSON Codec : {0}".format(click.style(json_ver, fg='yellow', bold=True))) print(" MsgPack Codec : {0}".format(click.style(msgpack_ver, fg='yellow', bold=True))) print(" Twisted : {0}".format(click.style(tx_ver, fg='yellow', bold=True))) print(" Python : {0}/{1}".format(click.style(py_ver, fg='yellow', bold=True), click.style(py_ver_detail, fg='yellow', bold=True))) print("OS : {0}".format(click.style(platform.platform(), fg='yellow', bold=True))) print("Machine : {0}".format(click.style(platform.machine(), fg='yellow', bold=True))) print("")
def run_command_start(options): """ Subcommand "crossbar start". """ # do not allow to run more than one Crossbar.io instance # from the same Crossbar.io node directory # pid_data = check_is_running(options.cbdir) if pid_data: print("Crossbar.io is already running from node directory {} (PID {}).".format(options.cbdir, pid_data['pid'])) sys.exit(1) else: fp = os.path.join(options.cbdir, _PID_FILENAME) with open(fp, 'w') as fd: argv = options.argv options_dump = vars(options) del options_dump['func'] del options_dump['argv'] pid_data = { 'pid': os.getpid(), 'argv': argv, 'options': options_dump } fd.write("{}\n".format(json.dumps(pid_data, sort_keys=False, indent=3, separators=(',', ': ')))) # we use an Autobahn utility to import the "best" available Twisted reactor # reactor = install_reactor(options.reactor, options.debug) # remove node PID file when reactor exits # def remove_pid_file(): fp = os.path.join(options.cbdir, _PID_FILENAME) if os.path.isfile(fp): os.remove(fp) reactor.addSystemEventTrigger('after', 'shutdown', remove_pid_file) # start Twisted logging # from crossbar._logging import log_publisher, make_logger from crossbar._logging import start_logging, set_global_log_level set_global_log_level(options.loglevel) log = make_logger() if options.logdir: # We want to log to a file from crossbar._logging import make_legacy_daily_logfile_observer log_publisher.addObserver( make_legacy_daily_logfile_observer(options.logdir, options.loglevel)) else: # We want to log to stdout/stderr. from crossbar._logging import make_stdout_observer from crossbar._logging import make_stderr_observer if options.loglevel == "none": # Do no logging! pass elif options.loglevel == "error": # Error: Only print errors to stderr. log_publisher.addObserver(make_stdout_observer(show_source=False, format=options.logformat)) log_publisher.addObserver(make_stderr_observer(show_source=False, format=options.logformat)) elif options.loglevel == "warn": # Print warnings+ to stderr. log_publisher.addObserver(make_stdout_observer(show_source=False, format=options.logformat)) log_publisher.addObserver(make_stderr_observer(show_source=False, format=options.logformat)) elif options.loglevel == "info": # Print info to stdout, warn+ to stderr log_publisher.addObserver(make_stdout_observer(show_source=False, format=options.logformat)) log_publisher.addObserver(make_stderr_observer(show_source=False, format=options.logformat)) elif options.loglevel == "debug": # Print debug+info to stdout, warn+ to stderr, with the class # source log_publisher.addObserver(make_stdout_observer(show_source=True, format=options.logformat)) log_publisher.addObserver(make_stderr_observer(show_source=True, format=options.logformat)) elif options.loglevel == "trace": # Print trace+, with the class source log_publisher.addObserver(make_stdout_observer(show_source=True, format=options.logformat, trace=True)) log_publisher.addObserver(make_stderr_observer(show_source=True, format=options.logformat)) else: assert False, "Shouldn't ever get here." # Actually start the logger. start_logging() for line in BANNER.splitlines(): log.info(click.style(("{:>40}").format(line), fg='yellow', bold=True)) bannerFormat = "{:>12} {:<24}" log.info(bannerFormat.format("Version:", click.style(crossbar.__version__, fg='yellow', bold=True))) # log.info(bannerFormat.format("Python:", click.style(platform.python_implementation(), fg='yellow', bold=True))) # log.info(bannerFormat.format("Reactor:", click.style(qual(reactor.__class__).split('.')[-1], fg='yellow', bold=True))) log.info(bannerFormat.format("Started:", click.style(utcnow(), fg='yellow', bold=True))) log.info() log.info("Starting from node directory {}".format(options.cbdir)) # create and start Crossbar.io node # def start_crossbar(): from crossbar.controller.node import Node node = Node(reactor, options) d = node.start() def on_error(err): log.error("Could not start node: {error}", error=err.value) if reactor.running: reactor.stop() d.addErrback(on_error) reactor.callWhenRunning(start_crossbar) try: log.info("Entering reactor event loop...") reactor.run() except Exception: log.failure("Could not start reactor: {log_failure.value}")
parser.add_argument( "--endpoint", type=str, default="tcp:8080", help= 'Twisted server endpoint descriptor, e.g. "tcp:8080" or "unix:/tmp/mywebsocket".' ) args = parser.parse_args() log.startLogging(sys.stdout) ## we use an Autobahn utility to install the "best" available Twisted reactor ## from autobahn.twisted.choosereactor import install_reactor reactor = install_reactor() print("Running on reactor {}".format(reactor)) ## create a WAMP router factory ## from autobahn.twisted.wamp import RouterFactory router_factory = RouterFactory() ## create a WAMP router session factory ## from autobahn.twisted.wamp import RouterSessionFactory session_factory = RouterSessionFactory(router_factory) ## create a WAMP-over-WebSocket transport server factory ## from autobahn.twisted.websocket import WampWebSocketServerFactory
def run_command_version(options): """ Subcommand "crossbar version". """ reactor = install_reactor(options.reactor, options.debug) # Python # py_ver = '.'.join([str(x) for x in list(sys.version_info[:3])]) if options.debug: py_ver += " [%s]" % sys.version.replace('\n', ' ') # Twisted / Reactor # tx_ver = "%s-%s" % (pkg_resources.require("Twisted")[0].version, reactor.__class__.__name__) if options.debug: tx_ver += " [%s]" % qual(reactor.__class__) # Autobahn # from autobahn.websocket.protocol import WebSocketProtocol ab_ver = pkg_resources.require("autobahn")[0].version if options.debug: ab_ver += " [%s]" % qual(WebSocketProtocol) # UTF8 Validator # from autobahn.websocket.utf8validator import Utf8Validator s = str(Utf8Validator) if 'wsaccel' in s: utf8_ver = 'wsaccel-%s' % pkg_resources.require('wsaccel')[0].version elif s.startswith('autobahn'): utf8_ver = 'autobahn' else: # could not detect UTF8 validator type/version utf8_ver = '?' if options.debug: utf8_ver += " [%s]" % qual(Utf8Validator) # XOR Masker # from autobahn.websocket.xormasker import XorMaskerNull s = str(XorMaskerNull) if 'wsaccel' in s: xor_ver = 'wsaccel-%s' % pkg_resources.require('wsaccel')[0].version elif s.startswith('autobahn'): xor_ver = 'autobahn' else: # could not detect XOR masker type/version xor_ver = '?' if options.debug: xor_ver += " [%s]" % qual(XorMaskerNull) # JSON Serializer # from autobahn.wamp.serializer import JsonObjectSerializer s = str(JsonObjectSerializer.JSON_MODULE) if 'ujson' in s: json_ver = 'ujson-%s' % pkg_resources.require('ujson')[0].version else: json_ver = 'stdlib' # MsgPack Serializer # try: import msgpack # noqa msgpack_ver = 'msgpack-python-%s' % pkg_resources.require('msgpack-python')[0].version except ImportError: msgpack_ver = '-' import crossbar import platform print("") print("Crossbar.io package versions and platform information:") print("") print("Crossbar.io : {0}".format(crossbar.__version__)) print("") print(" Autobahn|Python : {0}".format(ab_ver)) print(" WebSocket UTF8 Validator : {0}".format(utf8_ver)) print(" WebSocket XOR Masker : {0}".format(xor_ver)) print(" WAMP JSON Codec : {0}".format(json_ver)) print(" WAMP MsgPack Codec : {0}".format(msgpack_ver)) print(" Twisted : {0}".format(tx_ver)) print(" Python : {0}-{1}".format(py_ver, platform.python_implementation())) print("") print("OS : {0}".format(platform.platform())) print("Machine : {0}".format(platform.machine())) print("")