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_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('--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)
def _startlog(options): """ Start the logging in a way that all the subcommands can use it. """ from crossbar._logging import log_publisher, start_logging from crossbar._logging import set_global_log_level loglevel = getattr(options, "loglevel", "info") logformat = getattr(options, "logformat", "none") set_global_log_level(loglevel) if getattr(options, "logtofile", False): # We want to log to a file from crossbar._logging import make_logfile_observer if not options.logdir: logdir = options.cbdir else: logdir = options.logdir logfile = os.path.join(logdir, "node.log") if loglevel in ["error", "warn", "info"]: show_source = False else: show_source = True log_publisher.addObserver(make_logfile_observer(logfile, show_source)) else: # We want to log to stdout/stderr. from crossbar._logging import make_stdout_observer from crossbar._logging import make_stderr_observer if loglevel == "none": # Do no logging! pass elif loglevel in ["error", "warn", "info"]: # Print info to stdout, warn+ to stderr log_publisher.addObserver(make_stdout_observer(show_source=False, format=logformat)) log_publisher.addObserver(make_stderr_observer(show_source=False, format=logformat)) elif loglevel == "debug": # Print debug+info to stdout, warn+ to stderr, with the class # source log_publisher.addObserver(make_stdout_observer(show_source=True, format=logformat)) log_publisher.addObserver(make_stderr_observer(show_source=True, format=logformat)) elif loglevel == "trace": # Print trace+, with the class source log_publisher.addObserver(make_stdout_observer(show_source=True, format=logformat, trace=True)) log_publisher.addObserver(make_stderr_observer(show_source=True, format=logformat)) else: assert False, "Shouldn't ever get here." # Actually start the logger. start_logging()