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

cached   A

Complexity

Total Complexity 4

Size/Duplication

Total Lines 13
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 13
rs 10
wmc 4

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __call__() 0 8 3
A decorator() 0 6 2
A __init__() 0 2 1
1
#!/usr/bin/env python
2
3
from __future__ import print_function
4
from flask import Flask, request, abort
5
from werkzeug.contrib.cache import SimpleCache
6
import logging
7
import sys
8
import json
9
import geoip2.database
10
from tabulate import tabulate
11
12
try:
13
    import psycopg2
14
    from psycopg2 import extras as pgextras
15
except ImportError:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ImportError does not seem to be defined.
Loading history...
16
    print("psycopg2 needed to run server")
17
    sys.exit(1)
18
19
app = Flask(__name__)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable __name__ does not seem to be defined.
Loading history...
20
cache = SimpleCache()
21
logger = logging.getLogger(__name__)
22
23
24
CACHE_TIMEOUT = 300
25
26
27
class cached(object):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable object does not seem to be defined.
Loading history...
28
29
    def __init__(self, timeout=None):
30
        self.timeout = timeout or CACHE_TIMEOUT
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable CACHE_TIMEOUT does not seem to be defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable timeout does not seem to be defined.
Loading history...
31
32
    def __call__(self, f):
33
        def decorator(*args, **kwargs):
34
            response = cache.get(request.path)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable request does not seem to be defined.
Loading history...
35
            if response is None:
36
                response = f(*args, **kwargs)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable args does not seem to be defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable kwargs does not seem to be defined.
Loading history...
37
                cache.set(request.path, response, self.timeout)
38
            return response
39
        return decorator
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable decorator does not seem to be defined.
Loading history...
40
41
42
def dictToHstore(in_dict):
43
    single_kvs = []
44
    for key in in_dict.iterkeys():
45
        if in_dict[key]:
46
            single_kvs.append("{0}=>{1}".format(key, in_dict[key]))
47
    hstore_str = "'{0}'".format(", ".join(single_kvs))
48
    return hstore_str
49
50
51
@app.route("/rules", methods=["GET"])
52
@cached()
53
def get_rules():
54
    try:
55
        connv = psycopg2.connect("dbname=traceroutedb user=postgres host=localhost")
56
    except psycopg2.OperationalError as e:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable psycopg2 does not seem to be defined.
Loading history...
57
        logging.error(str(e))
58
        abort(503)
59
    cur = connv.cursor()
60
    cur.execute("SELECT * FROM endpoints;")
61
    rows = cur.fetchall()
62
    connv.close()
63
    return json.dumps(rows)
64
65
66
@app.route("/trace", methods=["POST"])
67
def receive_traces():
68
    config = app.config["trdb"]
69
    if config.get("mmdb", False):
70
        reader = config["mmdb"]
71
72
    if request.method == "POST":
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable request does not seem to be defined.
Loading history...
73
        cur = conn.cursor()
74
        data = request.get_json(force=True)
75
76
        trace = data["data"]
77
        reporter = data["reporter"]
78
        kvs = {"note": data.get("note"), "ext_ip": data.get("ext_ip")}
79
80
        if config.debug:
81
            print("SELECT nextval('traceroute_id_seq');")
82
            trace_id = "DEBUG_ID"
83
        else:
84
            try:
85
                cur.execute("SELECT nextval('traceroute_id_seq');")
86
            except psycopg2.ProgrammingError as e:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable psycopg2 does not seem to be defined.
Loading history...
87
                logging.error(str(e))
88
                conn.rollback()
89
                abort(503)
90
            trace_id = cur.fetchone()[0]
91
92
        trace_sql = "INSERT INTO traceroute VALUES ({0}, '{1}', '{2}', now(), '{3}', {4}::HSTORE);".format(trace_id, trace["src_ip"], trace["dst_ip"], reporter, dictToHstore(kvs))
93
        if config.debug:
94
            print(trace_sql)
95
        else:
96
            try:
97
                cur.execute(trace_sql)
98
            except psycopg2.ProgrammingError as e:
99
                logging.error(str(e))
100
                conn.rollback()
101
                abort(503)
102
103
        hops = trace["hops"]
104
        for key in hops.keys():
105
            hop = hops[key]
106
            for probe in hop:
107
                if probe["ip"] is None or probe["rtt"] is None:
108
                    continue
109
                else:
110
                    try:
111
                        probe["isp"] = reader.isp(probe["ip"])
112
                    except:
113
                        pass
0 ignored issues
show
Unused Code introduced by
This except handler seems to be unused and could be removed.

Except handlers which only contain pass and do not have an else clause can usually simply be removed:

try:
    raises_exception()
except:  # Could be removed
    pass
Loading history...
114
115
                kvs = {}
116
                time = probe.get("rtt", None)
117
                if time is not None and time != "None":
118
                    kvs["time"] = time
119
120
                anno = probe.get("anno", None)
121
                if anno:
122
                    kvs["anno"] = anno
123
124
                try:
125
                    asn = probe.get("isp").raw.get("autonomous_system_number", None)
126
                    if asn:
127
                        kvs["asn"] = asn
128
                except:
129
                    pass
0 ignored issues
show
Unused Code introduced by
This except handler seems to be unused and could be removed.

Except handlers which only contain pass and do not have an else clause can usually simply be removed:

try:
    raises_exception()
except:  # Could be removed
    pass
Loading history...
130
131
                kvs = dictToHstore(kvs)
132
                if config.debug:
133
                    print("INSERT INTO hop VALUES (nextval('probe_id_seq'), {0}, {1}, {2}, '{3}', now());".format(trace_id, key, kvs, probe["ip"]))
134
                else:
135
                    try:
136
                        cur.execute("INSERT INTO hop VALUES (nextval('probe_id_seq'), {0}, {1}, {2}, '{3}', now());".format(trace_id, key, kvs, probe["ip"]))
137
                    except psycopg2.ProgrammingError as e:
138
                        logging.error(str(e))
139
                        conn.rollback()
140
                        abort(503)
141
    conn.commit()
142
    cur.close()
143
    conn.commit()
144
    print("ip:", request.remote_addr, "submitted result for:", trace["src_ip"], ">", trace["dst_ip"], "trace_id:", trace_id)
0 ignored issues
show
introduced by
The variable trace does not seem to be defined in case request.method == "POST" on line 72 is False. Are you sure this can never be the case?
Loading history...
introduced by
The variable trace_id does not seem to be defined in case request.method == "POST" on line 72 is False. Are you sure this can never be the case?
Loading history...
145
    return 'OK'
146
147
148
@app.route("/trace/<trace_id>", methods=["GET"])
149
def lookup_trace(trace_id):
150
    cur = conn.cursor(cursor_factory=pgextras.DictCursor)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable pgextras does not seem to be defined.
Loading history...
151
    pgextras.register_hstore(cur)
152
    cur.execute("SELECT * FROM trv_trace WHERE traceroute_id = {id} ORDER BY traceroute_id,hop_number;".format(id=trace_id))
153
    rows = cur.fetchall()
154
    if "table" in request.args:
155
        headers = ["reporter", "traceroute_id", "origin_ip", "dest_ip", "probe_id",
156
                   "hop_number", "host", "hop_kvs"]
157
        return tabulate(rows, headers, tablefmt="psql")
158
    else:
159
        out = [json.dumps(x) for x in rows]
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable x does not seem to be defined.
Loading history...
160
        return "\n".join(out)
161
162
163
try:
164
    conn = psycopg2.connect("dbname=traceroutedb user=postgres host=localhost")
165
except psycopg2.OperationalError as e:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable psycopg2 does not seem to be defined.
Loading history...
166
    logging.error(str(e))
167
    sys.exit(1)
168
169
170
def run_server(config, app=app):
0 ignored issues
show
Comprehensibility Bug introduced by
app is re-defining a name which is already available in the outer-scope (previously defined on line 19).

It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior:

param = 5

class Foo:
    def __init__(self, param):   # "param" would be flagged here
        self.param = param
Loading history...
Comprehensibility Best Practice introduced by
The variable app does not seem to be defined.
Loading history...
171
    if config.debug:
172
        logger.setLevel(logging.DEBUG)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable logging does not seem to be defined.
Loading history...
173
174
    app.config["trdb"] = config
175
    if config.mmdb:
176
        reader = geoip2.database.Reader(config.mmdb)
177
        app.config["trdb"]["mmdb"] = reader
178
179
    app.run(port=9001, debug=config.debug, host='::')
180