diff --git a/lib/exabgp/__main__.py b/lib/exabgp/__main__.py index 3efa66d78..f42bb17a3 100644 --- a/lib/exabgp/__main__.py +++ b/lib/exabgp/__main__.py @@ -1,3 +1,4 @@ if __name__ == '__main__': from exabgp.application.main import main + main() diff --git a/lib/exabgp/application/bgp.py b/lib/exabgp/application/bgp.py deleted file mode 100755 index 0780b47d9..000000000 --- a/lib/exabgp/application/bgp.py +++ /dev/null @@ -1,446 +0,0 @@ -# encoding: utf-8 -""" -bgp.py - -Created by Thomas Mangin on 2009-08-30. -Copyright (c) 2009-2017 Exa Networks. All rights reserved. -License: 3-clause BSD. (See the COPYRIGHT file) -""" - -import os -import sys -import stat -import platform -import syslog -import string - -from exabgp.util.dns import warn -from exabgp.logger import Logger - -from exabgp.version import version - -# import before the fork to improve copy on write memory savings -from exabgp.reactor.loop import Reactor - -from exabgp.vendoring import docopt - -from exabgp.configuration.usage import usage - -from exabgp.debug import setup_report - -setup_report() - - -def is_bgp(s): - return all(c in string.hexdigits or c == ':' for c in s) - - -def __exit(memory, code): - if memory: - from exabgp.vendoring import objgraph - - sys.stdout.write('memory utilisation\n\n') - sys.stdout.write(objgraph.show_most_common_types(limit=20)) - sys.stdout.write('\n\n\n') - sys.stdout.write('generating memory utilisation graph\n\n') - sys.stdout.write() - obj = objgraph.by_type('Reactor') - objgraph.show_backrefs([obj], max_depth=10) - sys.exit(code) - - -def named_pipe(root, pipename='exabgp'): - locations = [ - '/run/exabgp/', - '/run/%d/' % os.getuid(), - '/run/', - '/var/run/exabgp/', - '/var/run/%d/' % os.getuid(), - '/var/run/', - root + '/run/exabgp/', - root + '/run/%d/' % os.getuid(), - root + '/run/', - root + '/var/run/exabgp/', - root + '/var/run/%d/' % os.getuid(), - root + '/var/run/', - ] - for location in locations: - cli_in = location + pipename + '.in' - cli_out = location + pipename + '.out' - - try: - if not stat.S_ISFIFO(os.stat(cli_in).st_mode): - continue - if not stat.S_ISFIFO(os.stat(cli_out).st_mode): - continue - except KeyboardInterrupt: - raise - except Exception: - continue - os.environ['exabgp_cli_pipe'] = location - return [location] - return locations - - -def root_folder(options, locations): - if options['--root']: - return os.path.realpath(os.path.normpath(options['--root'])).rstrip('/') - - argv = os.path.realpath(os.path.normpath(os.path.join(os.getcwd(), sys.argv[0]))) - - for location in locations: - if argv.endswith(location): - return argv[: -len(location)] - return '' - - -def get_envfile(options, etc): - envfile = 'exabgp.env' if not options["--env"] else options["--env"] - if not envfile.startswith('/'): - envfile = '%s/%s' % (etc, envfile) - return envfile - - -def get_env(envfile): - from exabgp.configuration.setup import environment - - try: - return environment.setup(envfile) - except environment.Error as exc: - sys.stdout.write(usage) - sys.stdout.flush() - print('\nconfiguration issue,', str(exc)) - sys.exit(1) - - -def main(): - major = int(sys.version[0]) - minor = int(sys.version[2]) - - if major <= 2 and minor < 5: - sys.stdout.write('This program can not work (is not tested) with your python version (< 2.5)\n') - sys.stdout.flush() - sys.exit(1) - - cli_named_pipe = os.environ.get('exabgp_cli_pipe', '') - if cli_named_pipe: - from exabgp.application.control import main as control - - control(cli_named_pipe) - sys.exit(0) - - options = docopt.docopt(usage, help=False) - - if options["--run"]: - sys.argv = sys.argv[sys.argv.index('--run') + 1 :] - if sys.argv[0] == 'healthcheck': - from exabgp.application import run_healthcheck - - run_healthcheck() - elif sys.argv[0] == 'cli': - from exabgp.application import run_cli - - run_cli() - else: - sys.stdout.write(usage) - sys.stdout.flush() - sys.exit(0) - return - - root = root_folder( - options, ['/bin/exabgp', '/sbin/exabgp', '/lib/exabgp/application/bgp.py', '/lib/exabgp/application/control.py'] - ) - prefix = '' if root == '/usr' else root - etc = prefix + '/etc/exabgp' - - os.environ['EXABGP_ETC'] = etc # This is not most pretty - - if options["--version"]: - sys.stdout.write('ExaBGP : %s\n' % version) - sys.stdout.write('Python : %s\n' % sys.version.replace('\n', ' ')) - sys.stdout.write('Uname : %s\n' % ' '.join(platform.uname()[:5])) - sys.stdout.write('Root : %s\n' % root) - sys.stdout.flush() - sys.exit(0) - - envfile = get_envfile(options, etc) - env = get_env(envfile) - - # Must be done before setting the logger as it modify its behaviour - if options["--debug"]: - env.log.all = True - env.log.level = syslog.LOG_DEBUG - - logger = Logger() - - from exabgp.configuration.setup import environment - - if options["--decode"]: - decode = ''.join(options["--decode"]).replace(':', '').replace(' ', '') - if not is_bgp(decode): - sys.stdout.write(usage) - sys.stdout.write('Environment values are:\n%s\n\n' % '\n'.join(' - %s' % _ for _ in environment.default())) - sys.stdout.write('The BGP message must be an hexadecimal string.\n\n') - sys.stdout.write('All colons or spaces are ignored, for example:\n\n') - sys.stdout.write(' --decode 001E0200000007900F0003000101\n') - sys.stdout.write(' --decode 001E:02:0000:0007:900F:0003:0001:01\n') - sys.stdout.write(' --decode FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF001E0200000007900F0003000101\n') - sys.stdout.write(' --decode FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:001E:02:0000:0007:900F:0003:0001:01\n') - sys.stdout.write(' --decode \'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 001E02 00000007900F0003000101\n\'') - sys.stdout.flush() - sys.exit(1) - else: - decode = '' - - duration = options["--signal"] - if duration and duration.isdigit(): - pid = os.fork() - if pid: - import time - import signal - - try: - time.sleep(int(duration)) - os.kill(pid, signal.SIGUSR1) - except KeyboardInterrupt: - pass - try: - pid, code = os.wait() - sys.exit(code) - except KeyboardInterrupt: - try: - pid, code = os.wait() - sys.exit(code) - except Exception: - sys.exit(0) - - if options["--help"]: - sys.stdout.write(usage) - sys.stdout.write('Environment values are:\n' + '\n'.join(' - %s' % _ for _ in environment.default())) - sys.stdout.flush() - sys.exit(0) - - if options["--decode"]: - env.log.parser = True - env.debug.route = decode - env.tcp.bind = '' - - if options["--profile"]: - env.profile.enable = True - if options["--profile"].lower() in ['1', 'true']: - env.profile.file = True - elif options["--profile"].lower() in ['0', 'false']: - env.profile.file = False - else: - env.profile.file = options["--profile"] - - if envfile and not os.path.isfile(envfile): - comment = 'environment file missing\ngenerate it using "exabgp --fi > %s"' % envfile - else: - comment = '' - - if options["--full-ini"] or options["--fi"]: - for line in environment.iter_ini(): - sys.stdout.write('%s\n' % line) - sys.stdout.flush() - sys.exit(0) - - if options["--full-env"] or options["--fe"]: - print() - for line in environment.iter_env(): - sys.stdout.write('%s\n' % line) - sys.stdout.flush() - sys.exit(0) - - if options["--diff-ini"] or options["--di"]: - for line in environment.iter_ini(True): - sys.stdout.write('%s\n' % line) - sys.stdout.flush() - sys.exit(0) - - if options["--diff-env"] or options["--de"]: - for line in environment.iter_env(True): - sys.stdout.write('%s\n' % line) - sys.stdout.flush() - sys.exit(0) - - if options["--once"]: - env.tcp.once = True - - if options["--pdb"]: - # The following may fail on old version of python (but is required for debug.py) - os.environ['PDB'] = 'true' - env.debug.pdb = True - - if options["--test"]: - env.debug.selfcheck = True - env.log.parser = True - - if options["--memory"]: - env.debug.memory = True - - configurations = [] - # check the file only once that we have parsed all the command line options and allowed them to run - if options[""]: - for f in options[""]: - # some users are using symlinks for atomic change of the configuration file - # using mv may however be better practice :p - normalised = os.path.realpath(os.path.normpath(f)) - target = os.path.realpath(normalised) - if os.path.isfile(target): - configurations.append(normalised) - continue - if f.startswith('etc/exabgp'): - normalised = os.path.join(etc, f[11:]) - if os.path.isfile(normalised): - configurations.append(normalised) - continue - - logger.debug('one of the arguments passed as configuration is not a file (%s)' % f, 'configuration') - sys.exit(1) - - else: - sys.stdout.write(usage) - sys.stdout.write('Environment values are:\n%s\n\n' % '\n'.join(' - %s' % _ for _ in environment.default())) - sys.stdout.write('no configuration file provided') - sys.stdout.flush() - sys.exit(1) - - from exabgp.bgp.message.update.attribute import Attribute - - Attribute.caching = env.cache.attributes - - if env.debug.rotate or len(configurations) == 1: - run(env, comment, configurations, root, options["--validate"]) - - if not (env.log.destination in ('syslog', 'stdout', 'stderr') or env.log.destination.startswith('host:')): - logger.error('can not log to files when running multiple configuration (as we fork)', 'configuration') - sys.exit(1) - - try: - # run each configuration in its own process - pids = [] - for configuration in configurations: - pid = os.fork() - if pid == 0: - run(env, comment, [configuration], root, options["--validate"], os.getpid()) - else: - pids.append(pid) - - # If we get a ^C / SIGTERM, ignore just continue waiting for our child process - import signal - - signal.signal(signal.SIGINT, signal.SIG_IGN) - - # wait for the forked processes - for pid in pids: - os.waitpid(pid, 0) - except OSError as exc: - logger.critical('can not fork, errno %d : %s' % (exc.errno, exc.strerror), 'reactor') - sys.exit(1) - - -def run(env, comment, configurations, root, validate, pid=0): - logger = Logger() - - logger.notice('Thank you for using ExaBGP', 'welcome') - logger.notice('%s' % version, 'version') - logger.notice('%s' % sys.version.replace('\n', ' '), 'interpreter') - logger.notice('%s' % ' '.join(platform.uname()[:5]), 'os') - logger.notice('%s' % root, 'installation') - - if comment: - logger.notice(comment, 'advice') - - warning = warn() - if warning: - logger.warning(warning, 'advice') - - if env.api.cli: - pipename = 'exabgp' if env.api.pipename is None else env.api.pipename - pipes = named_pipe(root, pipename) - if len(pipes) != 1: - env.api.cli = False - logger.error( - 'could not find the named pipes (%s.in and %s.out) required for the cli' % (pipename, pipename), 'cli' - ) - logger.error('we scanned the following folders (the number is your PID):', 'cli') - for location in pipes: - logger.error(' - %s' % location, 'cli control') - logger.error('please make them in one of the folder with the following commands:', 'cli control') - logger.error('> mkfifo %s/run/%s.{in,out}' % (os.getcwd(), pipename), 'cli control') - logger.error('> chmod 600 %s/run/%s.{in,out}' % (os.getcwd(), pipename), 'cli control') - if os.getuid() != 0: - logger.error( - '> chown %d:%d %s/run/%s.{in,out}' % (os.getuid(), os.getgid(), os.getcwd(), pipename), - 'cli control', - ) - else: - pipe = pipes[0] - os.environ['exabgp_cli_pipe'] = pipe - os.environ['exabgp_api_pipename'] = pipename - - logger.info('named pipes for the cli are:', 'cli control') - logger.info('to send commands %s%s.in' % (pipe, pipename), 'cli control') - logger.info('to read responses %s%s.out' % (pipe, pipename), 'cli control') - - if not env.profile.enable: - exit_code = Reactor(configurations).run(validate, root) - __exit(env.debug.memory, exit_code) - - try: - import cProfile as profile - except ImportError: - import profile - - if env.profile.file == 'stdout': - profiled = 'Reactor(%s).run(%s,"%s")' % (str(configurations), str(validate), str(root)) - exit_code = profile.run(profiled) - __exit(env.debug.memory, exit_code) - - if pid: - profile_name = "%s-pid-%d" % (env.profile.file, pid) - else: - profile_name = env.profile.file - - notice = '' - if os.path.isdir(profile_name): - notice = 'profile can not use this filename as output, it is not a directory (%s)' % profile_name - if os.path.exists(profile_name): - notice = 'profile can not use this filename as output, it already exists (%s)' % profile_name - - if not notice: - cwd = os.getcwd() - logger.debug('profiling ....', 'reactor') - profiler = profile.Profile() - profiler.enable() - try: - exit_code = Reactor(configurations).run(validate, root) - except Exception: - exit_code = Reactor.Exit.unknown - raise - finally: - from exabgp.vendoring import lsprofcalltree - - profiler.disable() - kprofile = lsprofcalltree.KCacheGrind(profiler) - try: - destination = profile_name if profile_name.startswith('/') else os.path.join(cwd, profile_name) - with open(destination, 'w+') as write: - kprofile.output(write) - except IOError: - notice = 'could not save profiling in formation at: ' + destination - logger.debug("-" * len(notice), 'reactor') - logger.debug(notice, 'reactor') - logger.debug("-" * len(notice), 'reactor') - __exit(env.debug.memory, exit_code) - else: - logger.debug("-" * len(notice), 'reactor') - logger.debug(notice, 'reactor') - logger.debug("-" * len(notice), 'reactor') - Reactor(configurations).run(validate, root) - __exit(env.debug.memory, 1) - - -if __name__ == '__main__': - main() diff --git a/lib/exabgp/application/cli.py b/lib/exabgp/application/cli.py index ab5a10d1c..05225cc3f 100644 --- a/lib/exabgp/application/cli.py +++ b/lib/exabgp/application/cli.py @@ -1,30 +1,23 @@ #!/usr/bin/env python # encoding: utf-8 -""" -cli.py -Created by Thomas Mangin on 2014-12-22. -Copyright (c) 2009-2017 Exa Networks. All rights reserved. -License: 3-clause BSD. (See the COPYRIGHT file) -""" +"""exabgp command line interface""" + import os import sys import time +import errno import select import signal -import errno +import argparse -from exabgp.application.bgp import root_folder -from exabgp.application.bgp import named_pipe -from exabgp.application.bgp import get_envfile -from exabgp.application.bgp import get_env +from exabgp.application.control import named_pipe from exabgp.application.control import check_fifo from exabgp.reactor.network.error import error from exabgp.reactor.api.response.answer import Answer -from exabgp.vendoring import docopt errno_block = set( ( @@ -40,27 +33,6 @@ ) ) -usage = """\ -The BGP swiss army knife of networking - -usage: exabgpcli [--root ROOT] -\t\t\t\t\t\t\t\t [--help|...] -\t\t\t\t\t\t\t\t [--env ENV] - -positional arguments: -\tcommand valid exabgpcli command (see below) - -optional arguments: -\t--env ENV, -e ENV environment configuration file -\t--help, -h exabgp manual page -\t--root ROOT, -f ROOT root folder where etc,bin,sbin are located - -commands: -\thelp show the commands known by ExaBGP -""".replace( - '\t', ' ' -) - class AnswerStream: done = '\n%s\n' % Answer.done @@ -123,29 +95,22 @@ def write_timeout(signum, frame): return writer +def args(sub): + # fmt:off + sub.add_argument('command', nargs='*', help='command to run on the router') + # fmt:on + + def main(): - options = docopt.docopt(usage, help=False) - if options['--env'] is None: - options['--env'] = '' - - root = root_folder(options, ['/bin/exabgpcli', '/sbin/exabgpcli', '/lib/exabgp/application/cli.py']) - prefix = '' if root == '/usr' else root - etc = prefix + '/etc/exabgp' - envfile = get_envfile(options, etc) - env = get_env(envfile) - pipename = env['api']['pipename'] + parser = argparse.ArgumentParser(description=sys.modules[__name__].__doc__) + args(parser) + cmdline(parser.parse_args()) - if options['--help']: - sys.stdout.write(usage) - sys.stdout.flush() - sys.exit(0) - if not options['']: - sys.stdout.write(usage) - sys.stdout.flush() - sys.exit(0) +def cmdline(cmdarg): + pipename = env['api']['pipename'] - command = ' '.join(options['']) + command = cmdarg.command pipes = named_pipe(root, pipename) if len(pipes) != 1: diff --git a/lib/exabgp/application/control.py b/lib/exabgp/application/control.py index 10a045c55..5838ddfe5 100644 --- a/lib/exabgp/application/control.py +++ b/lib/exabgp/application/control.py @@ -1,11 +1,3 @@ -""" -control.py - -Created by Thomas Mangin on 2015-01-13. -Copyright (c) 2015-2017 Exa Networks. All rights reserved. -License: 3-clause BSD. (See the COPYRIGHT file) -""" - import os import sys import fcntl @@ -23,6 +15,39 @@ mb = kb * 1024 +def named_pipe(root, pipename='exabgp'): + locations = [ + '/run/exabgp/', + '/run/%d/' % os.getuid(), + '/run/', + '/var/run/exabgp/', + '/var/run/%d/' % os.getuid(), + '/var/run/', + root + '/run/exabgp/', + root + '/run/%d/' % os.getuid(), + root + '/run/', + root + '/var/run/exabgp/', + root + '/var/run/%d/' % os.getuid(), + root + '/var/run/', + ] + for location in locations: + cli_in = location + pipename + '.in' + cli_out = location + pipename + '.out' + + try: + if not stat.S_ISFIFO(os.stat(cli_in).st_mode): + continue + if not stat.S_ISFIFO(os.stat(cli_out).st_mode): + continue + except KeyboardInterrupt: + raise + except Exception: + continue + os.environ['exabgp_cli_pipe'] = location + return [location] + return locations + + def env(app, section, name, default): r = os.environ.get('%s.%s.%s' % (app, section, name), None) if r is None: diff --git a/lib/exabgp/application/decode.py b/lib/exabgp/application/decode.py new file mode 100644 index 000000000..ada74fea7 --- /dev/null +++ b/lib/exabgp/application/decode.py @@ -0,0 +1,47 @@ +# encoding: utf-8 + +import sys +import string +import argparse + +from exabgp.environment import getenv + + +def is_bgp(message): + return all(c in string.hexdigits or c == ':' for c in message) + + +def args(sub): + # fmt:off + sub.add_argument('payload', help='the BGP payload in hexadecimal', type=str) + # fmt:on + + +def main(): + parser = argparse.ArgumentParser(description=sys.modules[__name__].__doc__) + args(parser) + cmdline(parser.parse_args()) + + +def cmdline(cmdarg): + if not is_bgp(cmdarg.payload): + # parser.print_usage() + sys.stdout.write('Environment values are:\n%s\n\n' % '\n'.join(' - %s' % _ for _ in Env.default())) + sys.stdout.write('The BGP message must be an hexadecimal string.\n\n') + sys.stdout.write('All colons or spaces are ignored, for example:\n\n') + sys.stdout.write(' --decode 001E0200000007900F0003000101\n') + sys.stdout.write(' --decode 001E:02:0000:0007:900F:0003:0001:01\n') + sys.stdout.write(' --decode FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF001E0200000007900F0003000101\n') + sys.stdout.write(' --decode FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:001E:02:0000:0007:900F:0003:0001:01\n') + sys.stdout.write(' --decode \'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 001E02 00000007900F0003000101\n\'') + sys.stdout.flush() + sys.exit(1) + + env = getenv() + env.log.parser = True + env.debug.route = cmdarg.payload + env.tcp.bind = '' + + +if __name__ == '__main__': + main() diff --git a/lib/exabgp/application/environ.py b/lib/exabgp/application/environ.py new file mode 100644 index 000000000..babbbc557 --- /dev/null +++ b/lib/exabgp/application/environ.py @@ -0,0 +1,42 @@ +# encoding: utf-8 + +"""exabgp environement values""" + +import sys +import argparse + +from exabgp.environment import Env + + +def args(sub): + # fmt: off + sub.add_argument('-d', '--diff', help='show only the different from the defaults', action='store_true') + sub.add_argument('-e', '--env', help='display using environment (not ini)', action='store_true') + # fmt: on + + +def default(): + sys.stdout.write('\nEnvironment values are:\n') + sys.stdout.write('\n'.join(' %s' % _ for _ in Env.default())) + sys.stdout.flush() + + +def cmdline(cmdarg): + dispatch = { + True: Env.iter_env, + False: Env.iter_ini, + } + + for line in dispatch[cmdarg.env](cmdarg.diff): + sys.stdout.write('%s\n' % line) + sys.stdout.flush() + + +def main(): + parser = argparse.ArgumentParser(description=sys.modules[__name__].__doc__) + args(parser) + cmdline(parser.parse_args()) + + +if __name__ == '__main__': + main() diff --git a/lib/exabgp/application/healthcheck.py b/lib/exabgp/application/healthcheck.py index 5e2007013..52846eab4 100644 --- a/lib/exabgp/application/healthcheck.py +++ b/lib/exabgp/application/healthcheck.py @@ -68,7 +68,7 @@ def parse(): formatter = argparse.RawDescriptionHelpFormatter parser = argparse.ArgumentParser(description=sys.modules[__name__].__doc__, formatter_class=formatter) - #fmt: off + # fmt: off g = parser.add_mutually_exclusive_group() g.add_argument("--debug", "-d", action="store_true", default=False, help="enable debugging") g.add_argument("--no-ack", "-a", action="store_true", default=False, help="set for exabgp 3.4 or 4.x when exabgp.api.ack=false") @@ -116,7 +116,7 @@ def parse(): g.add_argument("--up-execute", metavar='CMD', type=str, action="append", help="execute CMD when the service becomes available") g.add_argument("--down-execute", metavar='CMD', type=str, action="append", help="execute CMD when the service becomes unavailable") g.add_argument("--disabled-execute", metavar='CMD', type=str, action="append", help="execute CMD when the service is disabled") - #fmt: on + # fmt: on options = parser.parse_args() if options.config is not None: @@ -509,6 +509,11 @@ def sigterm_handler(signum, frame): # pylint: disable=W0612,W0613 break +def cmdline(cmdarg): + sys.argv = [f'{sys.argv[0]} {sys.argv[1]}'] + sys.argv[2:] + main() + + def main(): """Entry point.""" options = parse() diff --git a/lib/exabgp/application/main.py b/lib/exabgp/application/main.py index 988b32a24..0a85c8acb 100644 --- a/lib/exabgp/application/main.py +++ b/lib/exabgp/application/main.py @@ -7,40 +7,73 @@ License: 3-clause BSD. (See the COPYRIGHT file) """ +import os import sys +import argparse -from exabgp.application import run_exabgp -from exabgp.application import run_exabmp -from exabgp.application import run_cli -from exabgp.application import run_healthcheck +from exabgp.application import cli +from exabgp.application import server +from exabgp.application import decode +from exabgp.application import environ +from exabgp.application import version +from exabgp.application import healthcheck def main(): - if len(sys.argv) == 1: - run_exabgp() - return - - if sys.argv[1] == 'bgp': - sys.argv = sys.argv[1:] - run_exabgp() - return - - if sys.argv[1] == 'bmp': - sys.argv = sys.argv[1:] - run_exabgp() - return - - if sys.argv[1] == 'healthcheck': - sys.argv = sys.argv[1:] - run_healthcheck() - return - - if sys.argv[1] == 'cli': - sys.argv = sys.argv[1:] - run_cli() - return - - run_exabgp() + cli_named_pipe = os.environ.get('exabgp_cli_pipe', '') + if cli_named_pipe: + from exabgp.application.control import main as control + + control(cli_named_pipe) + sys.exit(0) + + # compatibility with exabgp 4.x + if len(sys.argv) > 1 and not ('-h' in sys.argv or '--help' in sys.argv): + if sys.argv[1] not in ('version', 'cli', 'healthcheck', 'decode', 'server', 'env'): + sys.argv = sys.argv[0:1] + ['server'] + sys.argv[1:] + + formatter = argparse.RawDescriptionHelpFormatter + parser = argparse.ArgumentParser(description='The BGP swiss army knife of networking') + + subparsers = parser.add_subparsers() + + sub = subparsers.add_parser('version', help='report exabgp version', description=version.__doc__) + sub.set_defaults(func=version.cmdline) + version.args(sub) + + sub = subparsers.add_parser('cli', help='control a running exabgp server instance', description=cli.__doc__) + sub.set_defaults(func=cli.cmdline) + cli.args(sub) + + sub = subparsers.add_parser( + 'healthcheck', + help='monitor services and announce/withdraw routes', + description=healthcheck.__doc__, + formatter_class=formatter, + ) + sub.set_defaults(func=healthcheck.cmdline) + # healthcheck.args(sub) + + sub = subparsers.add_parser('env', help='show exabgp configuration information', description=environ.__doc__) + sub.set_defaults(func=environ.cmdline) + environ.args(sub) + + sub = subparsers.add_parser('decode', help='decode hex-encoded bgp packets', description=decode.__doc__) + sub.set_defaults(func=decode.cmdline) + decode.args(sub) + + sub = subparsers.add_parser('server', help='start exabgp', description=server.__doc__) + sub.set_defaults(func=server.cmdline) + server.args(sub) + + cmdarg = parser.parse_args() + options = vars(cmdarg) + + if 'func' in options: + cmdarg.func(cmdarg) + else: + parser.print_help() + environ.default() if __name__ == '__main__': diff --git a/lib/exabgp/application/server.py b/lib/exabgp/application/server.py new file mode 100755 index 000000000..3e07fd900 --- /dev/null +++ b/lib/exabgp/application/server.py @@ -0,0 +1,277 @@ +# encoding: utf-8 + +"""exabgp server""" + + +import os +import sys +import syslog +import argparse +import platform + +from exabgp.debug import setup_report + +# import before the fork to improve copy on write memory savings +from exabgp.reactor.loop import Reactor + +from exabgp.util.dns import warn +from exabgp.logger import Logger + +# this is imported from configuration.setup to make sure it was initialised +from exabgp.environment import getenv +from exabgp.environment import ENVFILE +from exabgp.environment import ROOT + +from exabgp.application.control import named_pipe +from exabgp.version import version + + +def __exit(memory, code): + if memory: + from exabgp.vendoring import objgraph + + sys.stdout.write('memory utilisation\n\n') + sys.stdout.write(objgraph.show_most_common_types(limit=20)) + sys.stdout.write('\n\n\n') + sys.stdout.write('generating memory utilisation graph\n\n') + sys.stdout.write() + obj = objgraph.by_type('Reactor') + objgraph.show_backrefs([obj], max_depth=10) + sys.exit(code) + + +def args(sub): + # fmt:off + sub.add_argument('-t', '--test', help='perform a configuration validity check only', action='store_true') + sub.add_argument('-d', '--debug', help='start the python debugger on serious logging and on SIGTERM (shortcut for exabgp.log.all=true exabgp.log.level=DEBUG)', action='store_true') + sub.add_argument('-s', '--signal', help='issue a SIGUSR1 to reload the configuration after