1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 __doc__=''' ZenPing
16
17 Determines the availability of an IP address using ping.
18
19 $Id$'''
20
21 from socket import gethostbyname, getfqdn, gaierror
22
23 import time
24
25 import Globals
26
27 from Products.ZenStatus.AsyncPing import Ping
28 from Products.ZenStatus.TestPing import Ping as TestPing
29 from Products.ZenStatus import pingtree
30 from Products.ZenUtils.Utils import unused
31 unused(pingtree)
32
33 from Products.ZenEvents.ZenEventClasses import Status_Ping, Clear
34 from Products.ZenHub.PBDaemon import FakeRemote, PBDaemon
35 from Products.ZenUtils.DaemonStats import DaemonStats
36 from Products.ZenUtils.Driver import drive, driveLater
37
38 from twisted.internet import reactor
39 from twisted.python import failure
40
42
43 name = agent = "zenping"
44 eventGroup = "Ping"
45 initialServices = PBDaemon.initialServices + ['PingConfig']
46
47 pingTimeOut = 1.5
48 pingTries = 2
49 pingChunk = 75
50 pingCycleInterval = 60
51 configCycleInterval = 20*60
52 maxPingFailures = 2
53
54 pinger = None
55 pingTreeIter = None
56 startTime = None
57 jobs = 0
58 reconfigured = True
59 loadingConfig = None
60 lastConfig = None
61
62
71
72
84
85
88
89
94
95
101
102
104 "Send an event based on a ping job to the event backend."
105 evt = dict(device=pj.hostname,
106 ipAddress=pj.ipaddr,
107 summary=pj.message,
108 severity=pj.severity,
109 eventClass=Status_Ping,
110 eventGroup=self.eventGroup,
111 agent=self.agent,
112 component='',
113 manager=self.options.monitor)
114 evstate = getattr(pj, 'eventState', None)
115 if evstate is not None:
116 evt['eventState'] = evstate
117 self.sendEvent(evt)
118
120 "Get the configuration for zenping"
121 try:
122 if self.loadingConfig:
123 self.log.warning("Configuration still loading. Started at %s" %
124 time.asctime(time.localtime(self.loadingConfig)))
125 return
126
127 if self.lastConfig:
128 configwait = time.time() - self.lastConfig
129 delay = self.options.minconfigwait - configwait
130 if delay > 0:
131 reactor.callLater(delay, self.remote_updateConfig)
132 self.log.debug("Config recently updated: not fetching")
133 return
134
135 self.loadingConfig = time.time()
136
137 self.log.info('fetching monitor properties')
138 yield self.config().callRemote('propertyItems')
139 self.copyItems(driver.next())
140
141 driveLater(self.configCycleInterval, self.loadConfig)
142
143 self.log.info("fetching default RRDCreateCommand")
144 yield self.config().callRemote('getDefaultRRDCreateCommand')
145 createCommand = driver.next()
146
147 self.log.info("getting threshold classes")
148 yield self.config().callRemote('getThresholdClasses')
149 self.remote_updateThresholdClasses(driver.next())
150
151 self.log.info("getting collector thresholds")
152 yield self.config().callRemote('getCollectorThresholds')
153 self.rrdStats.config(self.options.monitor,
154 self.name,
155 driver.next(),
156 createCommand)
157
158 self.log.info("getting ping tree")
159 yield self.config().callRemote('getPingTree',
160 self.options.name,
161 findIp())
162 oldtree, self.pingtree = self.pingtree, driver.next()
163 self.clearDeletedDevices(oldtree)
164
165 self.rrdStats.gauge('configTime',
166 self.configCycleInterval,
167 time.time() - self.loadingConfig)
168 self.loadingConfig = None
169 self.lastConfig = time.time()
170 except Exception, ex:
171 self.log.exception(ex)
172
173
175 PBDaemon.buildOptions(self)
176 self.parser.add_option('--name',
177 dest='name',
178 default=getfqdn(),
179 help=("host that roots the ping dependency "
180 "tree: typically the collecting hosts' "
181 "name; defaults to our fully qualified "
182 "domain name (%s)" % getfqdn()))
183 self.parser.add_option('--test',
184 dest='test',
185 default=False,
186 action="store_true",
187 help="Run in test mode: doesn't really ping,"
188 " but reads the list of IP Addresses that "
189 " are up from /tmp/testping")
190 self.parser.add_option('--useFileDescriptor',
191 dest='useFileDescriptor',
192 default=None,
193 help=
194 "use the given (privileged) file descriptor")
195 self.parser.add_option('--minConfigWait',
196 dest='minconfigwait',
197 default=300,
198 type='int',
199 help=
200 "the minimal time, in seconds, "
201 "between refreshes of the config")
202
203
215
216
230
231 - def ping(self, pj):
237
244
245
247 "Note the end of the ping list with a successful status message"
248 runtime = time.time() - self.start
249 self.log.info("Finished pinging %d jobs in %.2f seconds",
250 self.jobs, runtime)
251 self.reconfigured = False
252 if not self.options.cycle:
253 reactor.stop()
254 else:
255 self.heartbeat()
256
267
277
279 try:
280 self.doPingFailed(err)
281 except Exception, ex:
282 import traceback
283 from StringIO import StringIO
284 out = StringIO()
285 traceback.print_exc(ex, out)
286 self.log.error("Exception: %s", out.getvalue())
287
321
322
327
328
343
344 d.addBoth(logResults)
345
346
348 items = dict(items)
349 for att in ("pingTimeOut",
350 "pingTries",
351 "pingChunk",
352 "pingCycleInterval",
353 "configCycleInterval",
354 "maxPingFailures",
355 ):
356 before = getattr(self, att)
357 after = items.get(att, before)
358 setattr(self, att, after)
359 self.configCycleInterval *= 60
360 self.reconfigured = True
361 self.getPinger()
362
363
369
370
379
380
385
386
388 try:
389 return gethostbyname(getfqdn())
390 except gaierror:
391
392 import os
393 import re
394 ifconfigs = ['/sbin/ifconfig',
395 '/usr/sbin/ifconfig',
396 '/usr/bin/ifconfig',
397 '/bin/ifconfig']
398 ifconfig = filter(os.path.exists, ifconfigs)[0]
399 fp = os.popen(ifconfig + ' -a')
400 config = fp.read().split('\n\n')
401 fp.close()
402 digits = r'[0-9]{1,3}'
403 pat = r'(addr:|inet) *(%s\.%s\.%s\.%s)[^0-9]' % ((digits,)*4)
404 parse = re.compile(pat)
405 results = []
406 for c in config:
407 addr = parse.search(c)
408 if addr:
409 results.append(addr.group(2))
410 try:
411 results.remove('127.0.0.1')
412 except ValueError:
413 pass
414 if results:
415 return results[0]
416 return '127.0.0.1'
417
418 if __name__=='__main__':
419 pm = ZenPing()
420 import logging
421 logging.getLogger('zen.Events').setLevel(20)
422 pm.run()
423