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

traceroutedb.TracerouteParser   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 88
Duplicated Lines 0 %
Metric Value
dl 0
loc 88
rs 10
wmc 21

6 Methods

Rating   Name   Duplication   Size   Complexity  
B TracerouteParser.parse_hdl() 0 19 5
A TracerouteParser.__str__() 0 7 2
A TracerouteParser.parse_data() 0 3 1
A TracerouteParser._parse_hop() 0 13 3
A TracerouteParser.__init__() 0 4 1
F TracerouteParser._parse_probe() 0 29 9
1
"""
2
A traceroute output parser, structuring the traceroute into a
3
sequence of hops, each containing individual probe results.
4
5
Courtesy of the Netalyzr project: http://netalyzr.icsi.berkeley.edu
6
"""
7
# ChangeLog
8
# ---------
9
#
10
# 1.0:  Initial release, tested on Linux/Android traceroute inputs only.
11
#       Also Python 2 only, most likely. (Send patches!)
12
#
13
# Copyright 2013 Christian Kreibich. All rights reserved.
14
#
15
# Redistribution and use in source and binary forms, with or without
16
# modification, are permitted provided that the following conditions are
17
# met:
18
#
19
#    1. Redistributions of source code must retain the above copyright
20
#       notice, this list of conditions and the following disclaimer.
21
#
22
#    2. Redistributions in binary form must reproduce the above
23
#       copyright notice, this list of conditions and the following
24
#       disclaimer in the documentation and/or other materials provided
25
#       with the distribution.
26
#
27
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
28
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
29
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30
# DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,
31
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
36
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37
# POSSIBILITY OF SUCH DAMAGE.
38
39
import cStringIO
40
import re
41
42
43
class Probe(object):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable object does not seem to be defined.
Loading history...
44
    """
45
    Abstraction of an individual probe in a traceroute.
46
    """
47
    def __init__(self):
48
        self.ipaddr = None
49
        self.name = None
50
        self.rtt = None  # RTT in ms
51
        self.anno = None  # Annotation, such as !H, !N, !X, etc
52
53
    def clone(self):
54
        """
55
        Return a copy of this probe, conveying the same endpoint.
56
        """
57
        copy = Probe()
58
        copy.ipaddr = self.ipaddr
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable self does not seem to be defined.
Loading history...
59
        copy.name = self.name
60
        return copy
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable copy does not seem to be defined.
Loading history...
61
62
63
class Hop(object):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable object does not seem to be defined.
Loading history...
64
    """
65
    A traceroute hop consists of a number of probes.
66
    """
67
    def __init__(self):
68
        self.idx = None  # Hop count, starting at 1
69
        self.probes = []  # Series of Probe instances
70
71
    def add_probe(self, probe):
72
        """Adds a Probe instance to this hop's results."""
73
        self.probes.append(probe)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable probe does not seem to be defined.
Loading history...
74
75
    def __str__(self):
76
        res = []
77
        last_probe = None
78
        for probe in self.probes:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable self does not seem to be defined.
Loading history...
79
            if probe.name is None:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable probe does not seem to be defined.
Loading history...
80
                res.append('*')
81
                continue
82
            anno = '' if probe.anno is None else ' ' + probe.anno
83
            if last_probe is None or last_probe.name != probe.name:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable last_probe does not seem to be defined.
Loading history...
84
                res.append('%s (%s) %1.3f ms%s' % (probe.name, probe.ipaddr,
85
                                                   probe.rtt, anno))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable anno does not seem to be defined.
Loading history...
86
            else:
87
                res.append('%1.3f ms%s' % (probe.rtt, anno))
88
            last_probe = probe
89
        return '  '.join(res)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable res does not seem to be defined.
Loading history...
90
91
92
class TracerouteParser(object):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable object does not seem to be defined.
Loading history...
93
    """
94
    A parser for traceroute text. A traceroute consists of a sequence of
95
    hops, each of which has at least one probe. Each probe records IP,
96
    hostname and timing information.
97
    """
98
    HEADER_RE = re.compile(r'traceroute to (\S+) \((\d+\.\d+\.\d+\.\d+)\)')
99
100
    def __init__(self):
101
        self.dest_ip = None
102
        self.dest_name = None
103
        self.hops = []
104
105
    def __str__(self):
106
        res = ['traceroute to %s (%s)' % (self.dest_name, self.dest_ip)]
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable self does not seem to be defined.
Loading history...
107
        ctr = 1
108
        for hop in self.hops:
109
            res.append('%2d  %s' % (ctr, str(hop)))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ctr does not seem to be defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable hop does not seem to be defined.
Loading history...
110
            ctr += 1
111
        return '\n'.join(res)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable res does not seem to be defined.
Loading history...
112
113
    def parse_data(self, data):
114
        """Parser entry point, given string of the whole traceroute output."""
115
        self.parse_hdl(cStringIO.StringIO(data))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable data does not seem to be defined.
Loading history...
116
117
    def parse_hdl(self, hdl):
118
        """Parser entry point, given readable file handle."""
119
        self.dest_ip = None
120
        self.dest_name = None
121
        self.hops = []
122
123
        for line in hdl:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable hdl does not seem to be defined.
Loading history...
124
            line = line.strip()
125
            if line == '':
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable line does not seem to be defined.
Loading history...
126
                continue
127
            if line.lower().startswith('traceroute'):
128
                # It's the header line at the beginning of the traceroute.
129
                mob = self.HEADER_RE.match(line)
130
                if mob:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable mob does not seem to be defined.
Loading history...
131
                    self.dest_ip = mob.group(2)
132
                    self.dest_name = mob.group(1)
133
            else:
134
                hop = self._parse_hop(line)
135
                self.hops.append(hop)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable hop does not seem to be defined.
Loading history...
136
137
    def _parse_hop(self, line):
138
        """Internal helper, parses a single line in the output."""
139
        parts = line.split()
140
        hop = Hop()
141
        hop.idx = int(parts.pop(0))
142
        probe = None
143
144
        while len(parts) > 0:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable parts does not seem to be defined.
Loading history...
145
            probe = self._parse_probe(parts, probe)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable probe does not seem to be defined.
Loading history...
146
            if probe:
147
                hop.add_probe(probe)
148
149
        return hop
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable hop does not seem to be defined.
Loading history...
150
151
    def _parse_probe(self, parts, last_probe=None):
152
        """Internal helper, parses the next probe's results from a line."""
153
        try:
154
            probe = Probe() if last_probe is None else last_probe.clone()
1 ignored issue
show
Comprehensibility Best Practice introduced by
The variable last_probe does not seem to be defined.
Loading history...
155
156
            tok1 = parts.pop(0)
157
            if tok1 == '*':
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable tok1 does not seem to be defined.
Loading history...
158
                return probe
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable probe does not seem to be defined.
Loading history...
159
160
            tok2 = parts.pop(0)
161
            if tok2 == 'ms':
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable tok2 does not seem to be defined.
Loading history...
162
                # This is an additional RTT for the same endpoint we
163
                # saw before.
164
                probe.rtt = float(tok1)
165
                if len(parts) > 0 and parts[0].startswith('!'):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable parts does not seem to be defined.
Loading history...
166
                    probe.anno = parts.pop(0)
167
            else:
168
                # This is a probe result from a different endpoint
169
                probe.name = tok1
170
                probe.ipaddr = tok2.translate(None, '()')
171
                probe.rtt = float(parts.pop(0))
172
                parts.pop(0)  # Drop "ms"
173
                if len(parts) > 0 and parts[0].startswith('!'):
174
                    probe.anno = parts.pop(0)
175
176
            return probe
177
178
        except (IndexError, ValueError):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ValueError does not seem to be defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable IndexError does not seem to be defined.
Loading history...
179
            return None
180