Package Products :: Package ZenStatus :: Package nmap :: Module PingResult |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenStatus.nmap.PingResult

      1  ##############################################################################
      2  #
      3  # Copyright (C) Zenoss, Inc. 2011, all rights reserved.
      4  #
      5  # This content is made available according to terms specified in
      6  # License.zenoss under the directory where your Zenoss product is installed.
      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()
     31
     32 -def parseNmapXml(input):
     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
     43 -def parseNmapXmlToDict(input):
     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
     52
     53 -class PingResult(object):
     54      """
     55      Model of an nmap ping/traceroute result.
     56      """
     57      interface.implements(interfaces.IPingResult)
     58
     59      @staticmethod
     60 -    def createNmapResult(hostTree):
     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
     87 -    def __init__(self, address, timestamp=None, isUp=False,
     88                   rtt=_NAN, stddev=_NAN, trace=_NO_TRACE):
     89          self._address = address
     90          self._timestamp = timestamp
     91          self._isUp = isUp
     92          self._rtt = rtt
     93          self._rttVariance = stddev * stddev
     94          self._trace = trace
     95
     96 -    def _parseTimestamp(self, hostTree):
     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
    112 -    def _parseTimes(self, hostTree):
    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 # given in milli sec, convert to secs
    122          rttVariance = timesNode.attrib['rttvar']
    123          rttVariance = float(rttVariance) / 1000.0
    124          return (rtt, rttVariance)
    125
    126 -    def _parseAddress(self, hostTree):
    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
    140 -    def _parseState(self, hostTree):
    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
    153 -    def _parseTraceroute(self, hostTree):
    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()  # no traceroute info in output file
    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
    173 -    def timestamp(self):
    174          """Timestamp of when ping was returned (seconds since epoch)."""
    175          return self._timestamp
    176
    177      @property
    178 -    def address(self):
    179          """Address of the host"""
    180          return self._address
    181
    182      @property
    183 -    def trace(self):
    184          """traceroute of the host"""
    185          return tuple(self._trace)
    186
    187 -    def getStatusString(self):
    188          """status string: up or down"""
    189          return _STATE_TO_STRING_MAP[self._isUp]
    190
    191 -    def __repr__(self):
    192          return "PingResult [%s, %s]" % (self._address, self.getStatusString())
    193
    194      @property
    195 -    def isUp(self):
    196          """true if host is up, false if host is down"""
    197          return self._isUp
    198
    199      @property
    200 -    def rtt(self):
    201          """round trip time aka ping time aka rtt; nan if host was down"""
    202          return self._rtt
    203
    204      @property
    205 -    def variance(self):
    206          """variance of the rtt; nan if host was down"""
    207          return self._rttVariance
    208
    209      @property
    210 -    def stddev(self):
    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