~oubiwann/adytum-collection/Libs

« back to all changes in this revision

Viewing changes to trunk/adytum/net/traceroute.py

  • Committer: Duncan McGreggor
  • Date: 2009-12-13 20:55:35 UTC
  • Revision ID: duncan@canonical.com-20091213205535-cwi94mtglzg51u7b
* Added more tests.
* Removed unneeded parameters.
* Fixed Multiple-response parsing.
* Added custom exception.
* Added error checking to the parse method.
* Added implementation for new methods and fixed implementations for old
* IP/Host methods.

Show diffs side-by-side

added added

removed removed

Lines of Context:
8
8
    TRACE_BIN = None
9
9
 
10
10
class Trace(object):
11
 
    '''
 
11
    """
12
12
    >>> from traceroute import Trace
13
13
    >>> t = Trace(host='www.adytum.us')
14
14
    >>> t.call()
15
15
    >>> len(t.results.getHosts()) > 0
16
 
    '''
 
16
    """
17
17
    def __init__(self, host='127.0.0.1', useDNS=False, queries='1'):
18
18
        self.host = host
19
19
        self.numeric = not useDNS
45
45
    run = _executeCommand
46
46
 
47
47
 
 
48
class ParseError(Exception):
 
49
    """
 
50
    There was a problem parsing the data.
 
51
    """
 
52
 
 
53
 
48
54
class ParseLine(object):
49
55
    """
50
56
    For a single-query traceroute, a hop can have either:
94
100
    ipMapPattern = re.compile("\(%s\)" % ipRegex)
95
101
    latencyPattern = re.compile("[0-9]+\.[0-9]+")
96
102
 
97
 
    def __init__(self, line, mode, queries):
 
103
    def __init__(self, line):
98
104
        self.line = line
99
 
        self.mode = mode
100
 
        self.queries = queries
101
105
        self.parsed = []
102
106
        self.parse()
103
107
 
157
161
    def parse(self):
158
162
        last_host_seen = ""
159
163
        parts = re.split("\s+", self.line.strip())
 
164
        maxCount = len(parts)
160
165
        hopIndex = parts.pop(0)
 
166
        count = 0
161
167
        while parts:
162
 
            data, parts = self._consume(hopIndex, parts)
 
168
            try:
 
169
                data, parts = self._consume(hopIndex, parts)
 
170
            except IndexError:
 
171
                raise ParseError("Invalid traceroute data: incomplete line.")
163
172
            self.parsed.append(data)
164
 
 
165
 
    def getHosts(self):
166
 
        pass
 
173
            count += 1
 
174
            if count > maxCount:
 
175
                raise ParseError("Invalid traceroute data; could not parse.")
 
176
 
 
177
    def getHopIndex(self):
 
178
        return int(self[0].get("hopIndex"))
 
179
 
 
180
    def getIPs(self):
 
181
        return [data.get("ipAddress") for data in self
 
182
                if data.get("ipAddress")]
167
183
 
168
184
    def getDomains(self, subdomains=0):
169
 
        pass
 
185
        return [data.get("domainName") for data in self 
 
186
                if data.get("domainName")]
170
187
 
171
 
    def getLatency(self):
172
 
        pass
 
188
    def getLatencies(self):
 
189
        return [float(data.get("latency")) for data in self
 
190
                if data.get("latency")]
173
191
 
174
192
    def getAverageLatency(self):
175
 
        pass
 
193
        latencies = self.getLatencies()
 
194
        return sum(latencies) / len(latencies)
176
195
 
177
196
 
178
197
class ParseOutput(object):
179
198
 
180
199
    def __init__(self, dataList, mode):
181
 
        # get rid of the first line, which is the command
 
200
        # Get rid of the summary line.
182
201
        self.data = dataList[1:]
183
202
        self.mode = mode
184
203
 
185
204
    def getHopCount(self):
186
205
        return len(self.data)
187
206
 
188
 
    def getHosts(self):
189
 
        full = [ParseLine(x, self.mode)['host'] for x in self.data]
190
 
        return [x for x in full if x]
191
 
 
192
 
    def getLinkedHosts(self):
193
 
        '''
 
207
    def getNextHop(self, thisIndex, hosts):
 
208
        nextIndex = thisIndex + 1
 
209
        try:
 
210
            return hosts[nextIndex]
 
211
        except IndexError:
 
212
            return
 
213
 
 
214
    def getIPs(self):
 
215
        ipGroups = []
 
216
        for line in self.data:
 
217
            ips = ParseLine(line).getIPs()
 
218
            if ips:
 
219
                ipGroups.append(ips)
 
220
        return ipGroups
 
221
 
 
222
    def getLinkedIPs(self):
 
223
        """
194
224
        This method provides a tuple of tuples of the pattern
195
225
            ((1,2), (2,3), (3,4) ... (n-1, n))
196
226
        for the purpose of using in graphs a la pydot.
197
 
        '''
198
 
        hosts = self.getHosts()
199
 
        return [(x, hosts[hosts.index(x)+1]) for x in hosts
200
 
                if len(hosts) > hosts.index(x) + 1]
 
227
        """
 
228
        nodes = []
 
229
        hosts = self.getIPs()
 
230
        for groupIndex, ipGroup in enumerate(self.getIPs()):
 
231
            nextHop = self.getNextHop(groupIndex, hosts)
 
232
            if not nextHop:
 
233
                continue
 
234
            for ip in ipGroup:
 
235
                for nextIp in nextHop:
 
236
                    node = (ip, nextIp)
 
237
                    try:
 
238
                        alreadyThere = nodes.index(node)
 
239
                    except ValueError:
 
240
                        nodes.append(node)
 
241
        return nodes
201
242
 
202
 
    def getLinkedDomains(self):
203
 
        '''
 
243
    def getLinkedDomains(self, limitSubdomains=0):
 
244
        """
204
245
        This does a similar thing as the getLinkedHosts() method. In fact,
205
246
        if the .mode attribute is numeric, it just calls getLinkedHosts().
206
247
        However, if not, pull out host and subdomain from the FQDN and only
207
248
        retain domain-level name info.
208
 
        '''
 
249
        """
209
250
        if self.mode == 'numeric':
210
251
            return self.getLinkedHosts()
211
252
        else: