Package ZenStatus :: Module Ping
[hide private]
[frames] | no frames]

Source Code for Module ZenStatus.Ping

  1  ########################################################################### 
  2  # 
  3  # This program is part of Zenoss Core, an open source monitoring platform. 
  4  # Copyright (C) 2007, Zenoss Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify it 
  7  # under the terms of the GNU General Public License version 2 as published by 
  8  # the Free Software Foundation. 
  9  # 
 10  # For complete information please visit: http://www.zenoss.com/oss/ 
 11  # 
 12  ########################################################################### 
 13   
 14  import sys 
 15  import os 
 16  import types 
 17  import time 
 18  import select 
 19  import socket 
 20  import ip 
 21  import icmp 
 22  import errno 
 23  import pprint 
 24  import logging 
 25  log = logging.getLogger("zen.Ping") 
 26   
 27   
28 -class PermissionError(Exception):
29 """Not permitted to access resource."""
30 31
32 -class PingJob:
33 """ 34 Class representing a single target to be pinged. 35 """
36 - def __init__(self, ipaddr, hostname="", status=0, cycle=60):
37 self.ipaddr = ipaddr 38 self.hostname = hostname 39 self.status = status 40 self.rtt = 0 41 self.start = 0 42 self.sent = 0 43 self.message = "" 44 self.severity = 5 45 self.inprocess = False 46 self.pathcheck = 0 47 self.eventState = 0
48 49
50 - def reset(self):
51 self.rrt = 0 52 self.start = 0 53 self.sent = 0 54 self.message = "" 55 self.severity = 5 56 self.inprocess = False 57 self.pathcheck = 0 58 self.eventState = 0
59 60
61 - def checkpath(self):
62 parent = getattr(self, "parent", False) 63 if parent: return parent.checkpath()
64 65
66 - def routerpj(self):
67 parent = getattr(self, "parent", False) 68 if parent: return parent.routerpj()
69 70 71 plog = logging.getLogger("zen.Ping")
72 -class Ping(object):
73 """ 74 Class that provides syncronous icmp ping. 75 """ 76
77 - def __init__(self, tries=2, timeout=2, chunkSize=10, fileDescriptor=None):
78 self.tries = tries 79 self.timeout = timeout 80 self.chunkSize = chunkSize 81 self.procId = os.getpid() 82 self.jobqueue = {} 83 self.pingsocket = None 84 self.morepkts = True 85 self.devcount = 0 86 self.pktdata = 'zenping %s %s' % (socket.getfqdn(), self.procId) 87 self.incount = self.outcount = 0 88 self.fileDescriptor = fileDescriptor
89 90
91 - def __del__(self):
92 if self.pingsocket and not self.fileDescriptor is None: 93 self.closePingSocket()
94 95
96 - def createPingSocket(self):
97 """make an ICMP socket to use for sending and receiving pings""" 98 sargs = socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP 99 if self.fileDescriptor is None: 100 try: 101 sock = socket.socket(*sargs) 102 except socket.error, e: 103 if e.args[0] == 1: 104 raise PermissionError("must be root to send icmp.") 105 else: 106 sock = socket.fromfd(self.fileDescriptor, *sargs) 107 # consume any packets that might be queued 108 while 1: 109 rd, wr, ex = select.select([sock], [], [], 0.) 110 if not rd: break 111 sock.recv(1024) 112 sock.setblocking(0) 113 self.pingsocket = sock
114 115
116 - def closePingSocket(self):
117 """unregister poll and close socket""" 118 self.pingsocket.close()
119 120
121 - def sendPackets(self):
122 """send numbtosend number of pingJobs and re""" 123 try: 124 numbtosend = self.chunkSize - len(self.jobqueue) 125 for i in range(numbtosend): 126 pingJob = self.sendqueue.next() 127 self.devcount += 1 128 self.sendPacket(pingJob) 129 if self.devcount % self.chunkSize == 0: 130 log.debug("Sent %d packets", self.devcount) 131 except StopIteration: self.morepkts = False
132 133
134 - def sendPacket(self, pingJob):
135 """Take a pingjob and send an ICMP packet for it""" 136 #### sockets with bad addresses fail 137 try: 138 pkt = icmp.Echo(id=self.procId, seq=pingJob.sent, data=self.pktdata) 139 buf = icmp.assemble(pkt) 140 pingJob.start = time.time() 141 plog.debug("send icmp to '%s'", pingJob.ipaddr) 142 self.pingsocket.sendto(buf, (pingJob.ipaddr, 0)) 143 pingJob.sent += 1 144 self.jobqueue[pingJob.ipaddr] = pingJob 145 except (SystemExit, KeyboardInterrupt): raise 146 except Exception, e: 147 pingJob.rtt = -1 148 pingJob.message = "%s sendto error %s" % (pingJob.ipaddr, e) 149 log.debug("Error sending ping: %s", e) 150 if hasattr(self, "reportPingJob"): 151 self.reportPingJob(pingJob)
152 153
154 - def recvPacket(self):
155 """receive a packet and decode its header""" 156 while 1: 157 try: 158 data = self.pingsocket.recv(1024) 159 if not data: return 160 ipreply = ip.disassemble(data) 161 icmppkt = icmp.disassemble(ipreply.data) 162 sip = ipreply.src 163 if (icmppkt.get_type() == icmp.ICMP_ECHOREPLY and 164 icmppkt.get_id() == self.procId and self.jobqueue.has_key(sip)): 165 plog.debug("echo reply pkt %s %s", sip, icmppkt) 166 self.pingJobSucceed(self.jobqueue[sip]) 167 elif icmppkt.get_type() == icmp.ICMP_UNREACH: 168 plog.debug("host unreachable pkt %s %s", sip, icmppkt) 169 try: 170 origpkt = icmppkt.get_embedded_ip() 171 dip = origpkt.dst 172 if (origpkt.data.find(self.pktdata) > -1 173 and self.jobqueue.has_key(dip)): 174 self.pingJobFail(self.jobqueue[dip]) 175 except ValueError: 176 plog.exception("failed to parse host unreachable packet") 177 else: 178 plog.debug("unexpected pkt %s %s", sip, icmppkt) 179 except (SystemExit, KeyboardInterrupt): raise 180 except socket.error, err: 181 err, errmsg = err.args 182 if err == errno.EAGAIN: 183 return 184 raise err 185 except Exception, e: 186 log.exception("receiving packet")
187 188 189
190 - def pingJobSucceed(self, pj):
191 """PingJob completed successfully. 192 """ 193 plog.debug("pj succeed for %s", pj.ipaddr) 194 pj.rtt = time.time() - pj.start 195 pj.message = "%s ip %s is up" % (pj.hostname, pj.ipaddr) 196 del self.jobqueue[pj.ipaddr] 197 if hasattr(self, "reportPingJob"): 198 self.reportPingJob(pj)
199 200
201 - def pingJobFail(self, pj):
202 """PingJob has failed remove from jobqueue. 203 """ 204 plog.debug("pj fail for %s", pj.ipaddr) 205 pj.rtt = -1 206 pj.message = "%s ip %s is down" % (pj.hostname, pj.ipaddr) 207 del self.jobqueue[pj.ipaddr] 208 if hasattr(self, "reportPingJob"): 209 self.reportPingJob(pj)
210 211
212 - def checkTimeouts(self):
213 """check to see if pingJobs in jobqueue have timed out""" 214 joblist = self.jobqueue.values() 215 #plog.debug("processing timeouts") 216 for pj in joblist: 217 if time.time() - pj.start > self.timeout: 218 if pj.sent >= self.tries: 219 plog.debug("pj timeout for %s", pj.ipaddr) 220 self.pingJobFail(pj) 221 else: 222 self.sendPacket(pj)
223 224
225 - def eventLoop(self, sendqueue):
226 startLoop = time.time() 227 plog.info("starting ping cycle %s" % (time.asctime())) 228 if type(sendqueue) == types.ListType: 229 self.sendqueue = iter(sendqueue) 230 else: 231 self.sendqueue = sendqueue 232 self.morepkts = True 233 self.devcount = 0 234 self.createPingSocket() 235 self.sendPackets() 236 while self.morepkts or len(self.jobqueue): 237 plog.debug("morepkts=%s jobqueue=%s",self.morepkts, 238 len(self.jobqueue)) 239 plog.debug("incount=%s outcount=%s", self.incount, self.outcount) 240 while 1: 241 data = select.select([self.pingsocket,], [], [], 0.1) 242 if data[0]: 243 self.recvPacket() 244 self.sendPackets() 245 else: break 246 self.checkTimeouts() 247 self.sendPackets() 248 self.closePingSocket() 249 plog.info("ping cycle complete %s" % (time.asctime())) 250 runtime = time.time() - startLoop 251 plog.info("pinged %d devices in %3.2f seconds" % 252 (self.devcount, runtime)) 253 return runtime
254 255
256 - def ping(self, ips):
257 """Perform async ping of a list of ips returns (goodips, badips). 258 """ 259 if type(ips) == types.StringType: ips = [ips,] 260 pjobs = map(lambda ip: PingJob(ip), ips) 261 self.eventLoop(pjobs) 262 goodips = [] 263 badips = [] 264 for pj in pjobs: 265 if pj.rtt >= 0: goodips.append(pj.ipaddr) 266 else: badips.append(pj.ipaddr) 267 return (goodips, badips)
268 269 270 if __name__ == "__main__": 271 ping = Ping() 272 logging.basicConfig() 273 log = logging.getLogger() 274 log.setLevel(10) 275 if len(sys.argv) > 1: targets = sys.argv[1:] 276 else: targets = ("127.0.0.1",) 277 good, bad = ping.ping(targets) 278 if good: print "Good ips: %s" % " ".join(good) 279 if bad: print "Bad ips: %s" % " ".join(bad) 280