1
2
3
4
5
6
7
8
9
10
11 __doc__ = """PingResult
12
13 Utilities to parse nmap output and represent results.
14 """
15
16 import collections
17 import math
18 from lxml import etree
19
20 import Globals
21 from zope import interface
22 from Products.ZenStatus import interfaces, TraceHop
23
24 import logging
25 from traceback import format_exc
26 log = logging.getLogger("zen.nmap")
27
28 _STATE_TO_STRING_MAP = { True: 'up', False: 'down'}
29 _NAN = float('nan')
30 _NO_TRACE = tuple()
33 """
34 Parse the XML output of nmap and return a list PingResults.
35 """
36 results = []
37 parseTree = etree.parse(input)
38 for hostTree in parseTree.xpath('/nmaprun/host'):
39 result = PingResult.createNmapResult(hostTree)
40 results.append(result)
41 return (results)
42
44 """
45 Parse the XML output of nmap and return a dict of PingResults indexed by IP.
46 """
47 rdict = {}
48 for result in parseNmapXml(input):
49 rdict[result.address] = result
50 return rdict
51
54 """
55 Model of an nmap ping/traceroute result.
56 """
57 interface.implements(interfaces.IPingResult)
58
59 @staticmethod
61 """
62 Contruct an PingResult from an XML parse tree for a host entry.
63 """
64 if getattr(hostTree, 'xpath', None) is None:
65 raise ValueError("hostTree must be of lxml.etree.Element type")
66 pr = PingResult("unknown")
67 pr._address = pr._parseAddress(hostTree)
68 pr._timestamp = pr._parseTimestamp(hostTree)
69 pr._isUp, reason = pr._parseState(hostTree)
70 if reason == 'localhost-response':
71 pr._rtt, pr._rttVariace = (0, 0)
72 else:
73 try:
74 pr._rtt, pr._rttVariance = pr._parseTimes(hostTree)
75 except Exception as ex:
76 traceback = format_exc()
77 log.debug("Error parsing times %s %s " % (ex, traceback))
78 pr._rtt, pr._rttVariace = (_NAN, _NAN)
79 try:
80 pr._trace = pr._parseTraceroute(hostTree)
81 except Exception as ex:
82 traceback = format_exc()
83 log.debug("Error parsing trace routes %s %s " % (ex, traceback))
84 pr._trace = _NO_TRACE
85 return pr
86
95
97 """
98 Extract timestamp if it exists.
99 """
100 try:
101 timestamp = None
102 starttime = hostTree.attrib.get('starttime', None)
103 if starttime is not None:
104 timestamp = int(starttime)
105 except KeyError:
106 return None
107 except Exception as ex:
108 traceback = format_exc()
109 log.debug("Error parsing timestamp %s %s " % (ex, traceback))
110 return timestamp
111
113 """
114 Extract round trip time from the hostTree.
115 """
116 times = hostTree.xpath('times')
117 if len(times) != 1:
118 raise ValueError("no times found for hostTree")
119 timesNode = times[0]
120 rtt = timesNode.attrib['srtt']
121 rtt = float(rtt) / 1000.0
122 rttVariance = timesNode.attrib['rttvar']
123 rttVariance = float(rttVariance) / 1000.0
124 return (rtt, rttVariance)
125
127 """
128 Extract the address (ip) from the hostTree.
129 """
130
131 addressNodes = hostTree.xpath("address[@addrtype='ipv4']")
132 if len(addressNodes) != 1:
133 addressNodes = hostTree.xpath("address[@addrtype='ipv6']")
134 if len(addressNodes) != 1:
135 raise ValueError("hostTree does not have address node")
136 addressNode = addressNodes[0]
137 address = addressNode.attrib['addr']
138 return address
139
141 """
142 Extract the host status from hostTree: return True if up, False if down.
143 """
144 statusNodes = hostTree.xpath('status')
145 if len(statusNodes) != 1:
146 raise ValueError("hostTree does not have status node")
147 statusNode = statusNodes[0]
148 state = statusNode.attrib['state']
149 isUp = state.lower() == 'up'
150 reason = statusNode.attrib.get('reason', 'unknown')
151 return (isUp, reason)
152
154 """
155 Extract the traceroute hops from hostTree in to a list that
156 preserves the hop order and saves the hop rtt.
157 """
158 hops = []
159 traceNodes = hostTree.xpath('trace')
160 if len(traceNodes) != 1:
161 return tuple()
162 traceNode = traceNodes[0]
163 hopNodes = traceNode.xpath('hop')
164 if len(hopNodes) < 1:
165 raise ValueError("hostTree does not have a trace/hop nodes")
166 for hopNode in hopNodes:
167 ipaddr = hopNode.attrib['ipaddr']
168 rtt = float(hopNode.attrib['rtt'])
169 hops.append(TraceHop(ip=ipaddr, rtt=rtt))
170 return hops
171
172 @property
174 """Timestamp of when ping was returned (seconds since epoch)."""
175 return self._timestamp
176
177 @property
179 """Address of the host"""
180 return self._address
181
182 @property
184 """traceroute of the host"""
185 return tuple(self._trace)
186
190
193
194 @property
196 """true if host is up, false if host is down"""
197 return self._isUp
198
199 @property
201 """round trip time aka ping time aka rtt; nan if host was down"""
202 return self._rtt
203
204 @property
206 """variance of the rtt; nan if host was down"""
207 return self._rttVariance
208
209 @property
211 """standard deviation of the rtt; nan if host was down"""
212 math.sqrt(self._rttVariance)
213
214 if __name__ == '__main__':
215
216 import os.path
217 nmap_testfile = os.path.dirname(
218 os.path.realpath(__file__)) + '/../tests/nmap_ping.xml'
219 results = parseNmapXml(nmap_testfile)
220 for result in results:
221 print result
222 for hop in result.trace:
223 print " ->", hop
224