1
2
3
4
5
6
7
8
9
10
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
29 """Not permitted to access resource."""
30
31
33 """
34 Class representing a single target to be pinged.
35 """
36 - def __init__(self, ipaddr, hostname="", status=0, cycle=60):
48
49
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
62 parent = getattr(self, "parent", False)
63 if parent: return parent.checkpath()
64
65
67 parent = getattr(self, "parent", False)
68 if parent: return parent.routerpj()
69
70
71 plog = logging.getLogger("zen.Ping")
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
94
95
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
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
117 """unregister poll and close socket"""
118 self.pingsocket.close()
119
120
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
135 """Take a pingjob and send an ICMP packet for it"""
136
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
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
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
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
213 """check to see if pingJobs in jobqueue have timed out"""
214 joblist = self.jobqueue.values()
215
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
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