Completed
Push — master ( c5f393...12467b )
by Ryan
01:17
created

run_runner()   D

Complexity

Conditions 10

Size

Total Lines 62

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 10
c 2
b 0
f 0
dl 0
loc 62
rs 4.6753

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 run_runner() 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
#!/usr/bin/env python
2
3
from __future__ import print_function
4
import sys
5
import json
6
import socket
7
import time
8
import tracerouteparser
9
import signal
10
from requests import post, get, ConnectionError
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in ConnectionError.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
11
from subprocess import check_output, Popen, PIPE
12
from multiprocessing import Pool
13
import logging
14
15
16
ON_POSIX = 'posix' in sys.builtin_module_names
17
18
19
def init_worker():
20
    signal.signal(signal.SIGINT, signal.SIG_IGN)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable signal does not seem to be defined.
Loading history...
21
22
23
def own_ips():
24
    return check_output(["ip", "-4", "addr", "list"])
25
26
27
global OWN_IPS
0 ignored issues
show
Best Practice introduced by
Using the global statement at the module level
Loading history...
28
OWN_IPS = own_ips()
29
30
31
def ext_ip():
32
    """
33
    We make the assumtion that we ony have one gateway to any one destination,
34
    could be better to use "ip route get $ip" but that won't cover NAT scenario
35
    """
36
    try:
37
        resp = get('https://httpbin.org/ip')
38
        return resp.json()["origin"]
39
    except ConnectionError as e:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ConnectionError does not seem to be defined.
Loading history...
40
        logging.warning("Could not get external ip:\n" + str(e))
41
        return None
42
43
44
def submit_trace(ip_dict, result):
45
    if result is None:
46
        return
47
    if ip_dict.get("simulate"):
48
        print(json.dumps(result))
49
    else:
50
        try:
51
            r = post(ip_dict["url"], data=json.dumps(result))
52
            logging.debug(r)
53
        except ConnectionError as e:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ConnectionError does not seem to be defined.
Loading history...
54
            print(e)
55
56
57
def run_trace(ip_dict):
58
    ip = ip_dict["ip"]
59
    if ip in OWN_IPS:
60
        return None
61
    try:
62
        proc = Popen(["/usr/sbin/traceroute", ip, "30"], stdout=PIPE, stderr=PIPE)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable PIPE does not seem to be defined.
Loading history...
63
        out, err = proc.communicate()
64
    except KeyboardInterrupt:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable KeyboardInterrupt does not seem to be defined.
Loading history...
65
        proc.terminate()
66
        proc.kill()
67
        return None
68
    src_ip = check_output(["ip", "route", "get", ip]).splitlines()[0].split()[-1]
69
    trp = tracerouteparser.TracerouteParser()
70
    trp.parse_data(out)
71
    data = {}
72
    data["dst_ip"] = trp.dest_ip
73
    data["src_ip"] = src_ip
74
    data["dst_name"] = trp.dest_name
75
    data["hops"] = {}
76
77
    for hop in trp.hops:
78
        data["hops"][int(hop.idx)] = []
79
        for probe in hop.probes:
80
            data["hops"][int(hop.idx)].append({"name": probe.name,
81
                                               "ip": probe.ipaddr,
82
                                               "rtt": probe.rtt,
83
                                               "anno": probe.anno})
84
    ret = {"reporter": socket.gethostname(),
85
           "note": ip_dict["note"],
86
           "ext_ip": ip_dict["ext_ip"],
87
           "data": data}
88
    submit_trace(ip_dict, ret)
89
90
91
def run_runner(config):
92
    NUMPROCS = config.procs
93
94
    if config.debug:
95
        logging.basicConfig(level=logging.DEBUG)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable logging does not seem to be defined.
Loading history...
96
        logging.getLogger("requests").setLevel(logging.DEBUG)
97
    else:
98
        logging.basicConfig(level=logging.WARNING)
99
        logging.getLogger("requests").setLevel(logging.WARNING)
100
    start_time = time.time()
101
102
    logging.info("Traceroute runner starting")
103
104
    if config.get("server_url", False):
105
        URL = config.server_url + "/trace"
106
    else:
107
        URL = "http://127.0.0.1:9001" + "/trace"
108
109
    if config.get("ips", False):
110
        ips = config.ips
111
        if config.get("ips_file", False):
112
            logging.warning("-i overrides ips from file with -f")
113
    elif config.ips_file:
114
        ips = []
115
        with open(config.ips_file) as f:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable f does not seem to be defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable config does not seem to be defined.
Loading history...
116
            for line in f:
117
                ips.append(line.strip())
118
    else:
119
        logging.error("No ips defined, exiting")
120
        sys.exit(1)
121
122
# The only reason we need to pack all this info into a list is that
123
# we only get to pass one iterable to function from map_async or
124
# thats how I (poorly) understand it
125
    ips_to_iter = []
126
    detected_ext_ip = ext_ip()
127
    for ip in ips:
0 ignored issues
show
introduced by
The variable ips does not seem to be defined for all execution paths.
Loading history...
128
        ip_dict = {}
129
        ip = str(ip)
130
        ip_dict["note"] = config.get("note", None)
131
        ip_dict["ext_ip"] = detected_ext_ip
132
        ip_dict["url"] = URL
133
        ip_dict["ip"] = ip
134
        ip_dict["simulate"] = config.get("simulate", None)
135
        ips_to_iter.append(ip_dict)
136
137
    logging.debug('IP addresses: ' + str(ips))
138
    logging.debug(str(ips_to_iter))
139
140
    try:
141
        pool = Pool(NUMPROCS, init_worker)
142
        pool.map_async(run_trace, ips_to_iter, 1).get(9999999)
143
        pool.close()
144
        pool.join()
145
    except KeyboardInterrupt:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable KeyboardInterrupt does not seem to be defined.
Loading history...
146
        print("Caught KeyboardInterrupt, terminating workers")
147
        pool.terminate()
148
        pool.join()
149
        sys.exit(1)
150
151
    end_time = time.time()
152
    logging.info("Traceroute runner done, Took: " + str(int(end_time - start_time)) + " seconds")
153