exabgp.application.server.run()   F
last analyzed

Complexity

Conditions 19

Size

Total Lines 100
Code Lines 82

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 19
eloc 82
nop 3
dl 0
loc 100
rs 0.5999
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like exabgp.application.server.run() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# encoding: utf-8
2
3
"""exabgp server"""
4
5
6
import os
7
import sys
8
import time
9
import signal
10
import syslog
11
import argparse
12
import platform
13
14
from exabgp.debug import trace_interceptor
15
16
# import before the fork to improve copy on write memory savings
17
from exabgp.reactor.loop import Reactor
18
19
from exabgp.util.dns import warn
20
from exabgp.logger import log
21
22
# this is imported from configuration.setup to make sure it was initialised
23
from exabgp.environment import getenv
24
from exabgp.environment import getconf
25
from exabgp.environment import ENVFILE
26
from exabgp.environment import ROOT
27
28
from exabgp.application.pipe import named_pipe
29
from exabgp.version import version
30
31
from exabgp.bgp.message.update.attribute import Attribute
32
33
34
def __exit(memory, code):
35
    if memory:
36
        from exabgp.vendoring import objgraph
37
38
        sys.stdout.write('memory utilisation\n\n')
39
        sys.stdout.write(objgraph.show_most_common_types(limit=20))
40
        sys.stdout.write('\n\n\n')
41
        sys.stdout.write('generating memory utilisation graph\n\n')
42
        sys.stdout.write()
43
        obj = objgraph.by_type('Reactor')
44
        objgraph.show_backrefs([obj], max_depth=10)
45
    sys.exit(code)
46
47
48
def _delayed_signal(delay, signalnum):
49
    if not delay:
50
        return
51
52
    pid = os.fork()
53
    if pid:
54
        # the parent process is the one waiting
55
        # and sending a signa to the child
56
        try:
57
            time.sleep(delay)
58
            os.kill(pid, signalnum)
59
        finally:
60
            try:
61
                pid, code = os.wait()
62
            finally:
63
                sys.exit(code)
64
65
66
def setargs(sub):
67
    # fmt:off
68
    sub.add_argument('-v', '--verbose', help='toogle all logging', action='store_true')
69
    sub.add_argument('-d', '--debug', help='start the python debugger on issue and (implies -v, -p)', action='store_true')
70
    sub.add_argument('-s', '--signal', help='issue a SIGUSR1 to reload the configuration after <time> seconds, only useful for code debugging', type=int)
71
    sub.add_argument('-1', '--once', help='only perform one attempt to connect to peers', action='store_true')
72
    sub.add_argument('-p', '--pdb', help='fire the debugger on critical logging, SIGTERM, and exceptions (shortcut for exabgp.pdb.enable=true)', action='store_true')
73
    sub.add_argument('-m', '--memory', help='display memory usage information on exit', action='store_true')
74
    sub.add_argument('--profile', help='enable profiling and set where the information should be saved', type=str, default='')
75
    sub.add_argument('configuration', help='configuration file(s)', nargs='+', type=str)
76
    # fmt:on
77
78
79
def cmdline(cmdarg):
80
    if not os.path.isfile(ENVFILE):
81
        comment = 'environment file missing\ngenerate it using "exabgp env --fi > %s"' % ENVFILE
82
    else:
83
        comment = ''
84
85
    env = getenv()
86
    # Must be done before setting the logger as it modify its behaviour
87
    if cmdarg.verbose or cmdarg.debug:
88
        env.log.all = True
89
        env.log.level = 'DEBUG'
90
        env.log.short = False
91
92
    if cmdarg.debug or cmdarg.pdb:
93
        env.debug.pdb = True
94
95
    log.init(env)
96
    trace_interceptor(env.debug.pdb)
97
98
    if cmdarg.profile:
99
        env.profile.enable = True
100
        env.profile.file = cmdarg.profile
101
102
    if cmdarg.once:
103
        env.tcp.once = True
104
105
    if cmdarg.memory:
106
        env.debug.memory = True
107
108
    if env.cache.attributes:
109
        Attribute.caching = env.cache.attributes
110
111
    configurations = []
112
    for configuration in cmdarg.configuration:
113
        location = getconf(configuration)
114
        if not location:
115
            log.critical(f'{configuration} is not an exabgp config file', 'configuration')
116
            sys.exit(1)
117
        configurations.append(location)
118
119
    delay = cmdarg.signal
120
    _delayed_signal(delay, signal.SIGUSR1)
121
122
    if env.debug.rotate or len(configurations) == 1:
123
        run(comment, configurations)
124
125
    if not (env.log.destination in ('syslog', 'stdout', 'stderr') or env.log.destination.startswith('host:')):
126
        log.error('can not log to files when running multiple configuration (as we fork)', 'configuration')
127
        sys.exit(1)
128
129
    try:
130
        # run each configuration in its own process
131
        pids = []
132
        for configuration in configurations:
133
            pid = os.fork()
134
            if pid == 0:
135
                run(comment, [configuration], os.getpid())
136
            else:
137
                pids.append(pid)
138
139
        # If we get a ^C / SIGTERM, ignore just continue waiting for our child process
140
        signal.signal(signal.SIGINT, signal.SIG_IGN)
141
142
        # wait for the forked processes
143
        for pid in pids:
144
            os.waitpid(pid, 0)
145
    except OSError as exc:
146
        log.critical('can not fork, errno %d : %s' % (exc.errno, exc.strerror), 'reactor')
147
        sys.exit(1)
148
149
150
def run(comment, configurations, pid=0):
151
    env = getenv()
152
153
    log.info('Thank you for using ExaBGP', 'welcome')
154
    log.debug('%s' % version, 'version')
155
    log.debug('%s' % ROOT, 'location')
156
    log.debug('%s' % sys.version.replace('\n', ' '), 'python')
157
    log.debug('%s' % ' '.join(platform.uname()[:5]), 'platform')
158
159
    if comment:
160
        log.error(comment, 'advice')
161
162
    warning = warn()
163
    if warning:
164
        log.warning(warning, 'advice')
165
166
    if env.api.cli:
167
        pipename = 'exabgp' if env.api.pipename is None else env.api.pipename
168
        pipes = named_pipe(ROOT, pipename)
169
        if len(pipes) != 1:
170
            env.api.cli = False
171
            log.error(
172
                'could not find the named pipes (%s.in and %s.out) required for the cli' % (pipename, pipename), 'cli'
173
            )
174
            log.error('we scanned the following folders (the number is your PID):', 'cli')
175
            for location in pipes:
176
                log.error(' - %s' % location, 'cli control')
177
            log.error('please make them in one of the folder with the following commands:', 'cli control')
178
            log.error('> mkfifo %s/run/%s.{in,out}' % (os.getcwd(), pipename), 'cli control')
179
            log.error('> chmod 600 %s/run/%s.{in,out}' % (os.getcwd(), pipename), 'cli control')
180
            if os.getuid() != 0:
181
                log.error(
182
                    '> chown %d:%d %s/run/%s.{in,out}' % (os.getuid(), os.getgid(), os.getcwd(), pipename),
183
                    'cli control',
184
                )
185
        else:
186
            pipe = pipes[0]
187
            os.environ['exabgp_cli_pipe'] = pipe
188
            os.environ['exabgp_api_pipename'] = pipename
189
190
            log.info('named pipes for the cli are:', 'cli control')
191
            log.info('to send commands  %s%s.in' % (pipe, pipename), 'cli control')
192
            log.info('to read responses %s%s.out' % (pipe, pipename), 'cli control')
193
194
    if not env.profile.enable:
195
        exit_code = Reactor(configurations).run()
196
        __exit(env.debug.memory, exit_code)
197
198
    try:
199
        import cProfile as profile
200
    except ImportError:
201
        import profile
202
203
    if env.profile.file == 'stdout':
204
        profiled = 'Reactor(%s).run()' % (str(configurations))
205
        exit_code = profile.run(profiled)
206
        __exit(env.debug.memory, exit_code)
207
208
    if pid:
209
        profile_name = "%s-pid-%d" % (env.profile.file, pid)
210
    else:
211
        profile_name = env.profile.file
212
213
    notice = ''
214
    if os.path.isdir(profile_name):
215
        notice = 'profile can not use this filename as output, it is not a directory (%s)' % profile_name
216
    if os.path.exists(profile_name):
217
        notice = 'profile can not use this filename as output, it already exists (%s)' % profile_name
218
219
    if not notice:
220
        cwd = os.getcwd()
221
        log.debug('profiling ....', 'reactor')
222
        profiler = profile.Profile()
223
        profiler.enable()
224
        try:
225
            exit_code = Reactor(configurations).run()
226
        except Exception:
227
            exit_code = Reactor.Exit.unknown
228
            raise
229
        finally:
230
            from exabgp.vendoring import lsprofcalltree
231
232
            profiler.disable()
233
            kprofile = lsprofcalltree.KCacheGrind(profiler)
234
            try:
235
                destination = profile_name if profile_name.startswith('/') else os.path.join(cwd, profile_name)
236
                with open(destination, 'w+') as write:
237
                    kprofile.output(write)
238
            except IOError:
239
                notice = 'could not save profiling in formation at: ' + destination
240
                log.debug("-" * len(notice), 'reactor')
241
                log.debug(notice, 'reactor')
242
                log.debug("-" * len(notice), 'reactor')
243
            __exit(env.debug.memory, exit_code)
244
    else:
245
        log.debug("-" * len(notice), 'reactor')
246
        log.debug(notice, 'reactor')
247
        log.debug("-" * len(notice), 'reactor')
248
        Reactor(configurations).run()
249
        __exit(env.debug.memory, 1)
250
251
252
def main():
253
    parser = argparse.ArgumentParser(description=sys.modules[__name__].__doc__)
254
    setargs(parser)
255
    cmdline(parser, parser.parse_args())
256
257
258
if __name__ == '__main__':
259
    main()
260