1
2
3
4
5
6
7
8
9
10
11 __doc__ = """PingTask
12
13 Determines the availability of a IP addresses using ping (ICMP).
14
15 """
16
17 import math
18 import re
19 import time
20 import logging
21 log = logging.getLogger("zen.zenping")
22
23 from twisted.python.failure import Failure
24 from twisted.internet import defer
25
26 import Globals
27 from zope import interface
28 from zope import component
29
30 from zenoss.protocols.protobufs.zep_pb2 import SEVERITY_CLEAR
31
32 from Products.ZenRRD.zencommand import Cmd, ProcessRunner, TimeoutError
33 from Products.ZenCollector import interfaces
34 from Products.ZenCollector.tasks import TaskStates, BaseTask
35
36 from Products.ZenUtils.Utils import unused
37 from Products.ZenCollector.services.config import DeviceProxy
38 unused(DeviceProxy)
39
40 from Products.ZenEvents.ZenEventClasses import Status_Ping
41 from Products.ZenEvents import Event
42 from Products.ZenEvents import ZenEventClasses
43 from zenoss.protocols.protobufs import zep_pb2 as events
44
45 from Products.ZenUtils.IpUtil import ipunwrap
46 from interfaces import IPingTask
47
48 COLLECTOR_NAME = "zenping"
49
50 STATUS_EVENT = {
51 'eventClass' : Status_Ping,
52 'component' : 'zenping',
53 ' eventGroup' : 'Ping'
54 }
55 SUPPRESSED = 2
56 _NAN = float('nan')
59 interface.implements(IPingTask)
60
61 STATE_PING_START = 'PING_START'
62 STATE_PING_STOP = 'PING_STOP'
63 STATE_STORE_PERF = 'STORE_PERF_DATA'
64
65 - def __init__(self, taskName, deviceId, scheduleIntervalSeconds, taskConfig):
66 """
67 @param deviceId: the Zenoss deviceId to watch
68 @type deviceId: string
69 @param taskName: the unique identifier for this task
70 @type taskName: string
71 @param scheduleIntervalSeconds: the interval at which this task will be
72 collected
73 @type scheduleIntervalSeconds: int
74 @param taskConfig: the configuration for this task
75 """
76 super(PingTask, self).__init__(
77 taskName, deviceId,
78 scheduleIntervalSeconds, taskConfig
79 )
80
81
82 self.name = taskName
83 self.configId = deviceId
84 self.state = TaskStates.STATE_IDLE
85
86
87 self._device = taskConfig
88 self._devId = deviceId
89 self._manageIp = ipunwrap(self._device.manageIp)
90 self.interval = scheduleIntervalSeconds
91 self._pingResult = None
92
93 self._trace = tuple()
94 self._isUp = None
95 self._daemon = component.queryUtility(interfaces.ICollector)
96 self._dataService = component.queryUtility(interfaces.IDataService)
97 self._eventService = component.queryUtility(interfaces.IEventService)
98 self._preferences = component.queryUtility(interfaces.ICollectorPreferences,
99 COLLECTOR_NAME)
100
101 self.config = self._device.monitoredIps[0]
102 self._iface = self.config.iface
103 self._lastErrorMsg = ''
104
105
106 self.pauseOnScheduled = False
107 self._rtt =[]
108
110 """
111 Contact to one device and return a deferred which gathers data from
112 the device.
113
114 @return: A task to ping the device and any of its interfaces.
115 @rtype: Twisted deferred object
116 """
117 raise NotImplementedError()
118
119
121 return self._pauseOnScheduled
122
124 self._pauseOnScheduled = value
125
126 pauseOnScheduled = property(fget=_getPauseOnScheduled, fset=_setPauseOnScheduled)
127 """Pause this task after it's been scheduled."""
128
130 """
131 After the task has been scheduled, set the task in to the PAUSED state.
132
133 @param scheduler: Collection Framework Scheduler
134 @type scheduler: IScheduler
135 """
136 if self.pauseOnScheduled:
137 scheduler.pauseTasksForConfig(self.configId)
138
139 @property
142
143 @property
145 """
146 Determine if the device is up
147 """
148 return self._calculateState()
149
151 """
152 Calculate if the device is up or down based on current ping statistics.
153 Return None if unknown, False if down, and True if up.
154 """
155
156
157 if len(self._rtt) <= 0:
158 return None
159
160 lostPackets = len([ rtt for rtt in self._rtt if math.isnan(rtt)])
161 totalPackets = len(self._rtt)
162 receivedPackets = totalPackets - lostPackets
163
164 isUp = receivedPackets > 0
165 return isUp
166
168 """
169 Clear out current ping statistics.
170 """
171 self._rtt =[]
172
174 """
175 Log the PingResult; set ping state, log to rrd.
176 """
177 if pingResult is None:
178 raise ValueError("pingResult can not be None")
179 self._rtt.append(pingResult.rtt)
180 if pingResult.trace:
181 self._trace = pingResult.trace
182
184 """
185 Send an event based on a ping job to the event backend.
186 """
187 msg = msgTpl % self._devId
188 evt = dict(
189 device=self._devId,
190 ipAddress=self.config.ip,
191 summary=msg,
192 severity=severity,
193 eventClass=ZenEventClasses.Status_Ping,
194 eventGroup='Ping',
195 component=self._iface,
196 )
197
198 if self._pingResult is not None:
199
200 if self._pingResult.timestamp:
201 evt['lastTime'] = evt['firstTime'] = self._pingResult.timestamp
202
203 if severity and self._pingResult.trace:
204 evt['lastTraceroute'] = str(self._pingResult.trace)
205
206 if rootCause:
207 evt['rootCause'] = rootCause
208 evt['eventState'] = SUPPRESSED
209
210
211 if self.config.ip == self._manageIp:
212 evt['isManageIp'] = True
213
214 self._eventService.sendEvent(evt)
215
216
217
218 if severity==events.SEVERITY_CLEAR and \
219 'isManageIp' in evt and evt['component']:
220 evt['component'] = ''
221 self._eventService.sendEvent(evt)
222
224 """
225 Send an ping up event to the event backend.
226 """
227 return self.sendPingEvent(msgTpl, events.SEVERITY_CLEAR)
228
229 - def sendPingDown(self, msgTpl='%s is DOWN!', rootCause=None):
230 """
231 Send an ping down event to the event backend.
232 """
233 return self.sendPingEvent(msgTpl, events.SEVERITY_CRITICAL, rootCause)
234
236 """
237 Store the datapoint results asked for by the RRD template.
238 """
239 if len(self._rtt) == 0:
240 return
241
242
243 rtts = [ rtt for rtt in self._rtt if math.isnan(rtt) == False ]
244 if rtts:
245 received = len(rtts)
246 pingCount = len(self._rtt)
247 minRtt = min(rtts)
248 maxRtt = max(rtts)
249 avgRtt = sum(rtts) / received
250 varianceRtt = sum([ math.pow(rtt - avgRtt, 2) for rtt in rtts ]) / received
251 stddevRtt = math.sqrt(varianceRtt)
252 pingLoss = 100.0
253 if pingCount > 0 :
254 pingLoss = (1 - (len(rtts) / pingCount)) * 100.0
255
256 datapoints = {
257 'rtt' : avgRtt,
258 'rtt_avg' : avgRtt,
259 'rtt_min' : minRtt,
260 'rtt_max' : maxRtt,
261 'rtt_losspct': pingLoss,
262 'rtt_stddev': stddevRtt,
263 'rcvCount': received,
264 }
265 else:
266 pingLoss = 100
267 datapoints = {
268 'rtt_losspct': pingLoss,
269 }
270
271 for rrdMeta in self.config.points:
272 name, path, rrdType, rrdCommand, rrdMin, rrdMax = rrdMeta
273 value = datapoints.get(name, None)
274 if value is None:
275 log.debug("No datapoint '%s' found on the %s pingTask",
276 name, self)
277 else:
278 self._dataService.writeRRD(
279 path, value, rrdType,
280 rrdCommand=rrdCommand,
281 min=rrdMin, max=rrdMax,
282 )
283