1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 __doc__ = """zenping
17
18 Determines the availability of a IP addresses using ping (ICMP).
19
20 """
21
22 import os
23 import os.path
24 import sys
25 import re
26 from socket import gethostbyname, getfqdn, gaierror
27 import time
28 import logging
29 log = logging.getLogger("zen.zenping")
30
31
32 from icmpecho.Ping import Ping4, Ping6
33
34 import Globals
35 import zope.interface
36 import zope.component
37 from Products.Five import zcml
38 import Products
39 import Products.Five
40 import Products.ZenStatus
41
42 from Products.ZenCollector.daemon import CollectorDaemon
43 from Products.ZenCollector.interfaces import ICollector, ICollectorPreferences,\
44 IConfigurationListener
45 from Products.ZenCollector.tasks import SimpleTaskFactory,\
46 SubConfigurationTaskSplitter
47 from Products.ZenStatus.interfaces import IPingCorrelatorTaskFactory
48
49 from Products.ZenUtils.IpUtil import ipunwrap
50
51 from Products.ZenStatus.PingService import PingService
52 from Products.ZenStatus.TestPing import Ping as TestPing
53 from Products.ZenStatus.TracerouteTask import TracerouteTask
54 from Products.ZenStatus.PingTask import PingCollectionTask
55 from Products.ZenStatus.NetworkModel import NetworkModel
56
57 from Products.ZenUtils.Utils import unused
58 from Products.ZenCollector.services.config import DeviceProxy
59 unused(DeviceProxy)
60 from Products.ZenHub.services.PingPerformanceConfig import PingPerformanceConfig
61 unused(PingPerformanceConfig)
62
63
64 COLLECTOR_NAME = "zenping"
65 TOPOLOGY_MODELER_NAME = "topology_modeler"
66 TOPOLOGY_CORRELATOR_NAME = "topology_correlator"
67 MAX_BACK_OFF_MINUTES = 20
68 MAX_IFACE_PING_JOBS = 10
69
70
72 zope.interface.implements(ICollectorPreferences)
73
102
104 parser.add_option('--showrawresults',
105 dest='showrawresults',
106 action="store_true",
107 default=False,
108 help="Show the raw RRD values. For debugging purposes only.")
109 parser.add_option('--name',
110 dest='name',
111 default=findIp(),
112 help=("Host that roots the ping dependency "
113 "tree: typically the collecting hosts' "
114 "name; defaults to our fully qualified "
115 "domain name (%default)"))
116 parser.add_option('--test',
117 dest='test',
118 default=False,
119 action="store_true",
120 help="Run in test mode: doesn't really ping,"
121 " but reads the list of IP Addresses that "
122 " are up from /tmp/testping")
123 parser.add_option('--maxbackoffminutes',
124 dest='maxbackoffminutes',
125 default=MAX_BACK_OFF_MINUTES,
126 type='int',
127 help="When a device fails to respond, increase the time to" \
128 " check on the device until this limit.")
129 parser.add_option('--tracetimeoutseconds',
130 dest='tracetimeoutseconds',
131 default=40,
132 type='int',
133 help="Wait for a maximum of this time before stopping"
134 " the traceroute")
135 parser.add_option('--tracechunk',
136 dest='tracechunk',
137 default=5,
138 type='int',
139 help="Number of devices to traceroute at once.")
140 parser.add_option('--ipv4topofile',
141 dest='ipv4topofile',
142 default='',
143 help="Use this file rather than the default for IPv4.")
144 parser.add_option('--ipv6topofile',
145 dest='ipv6topofile',
146 default='',
147 help="Use this file rather than the default for IPv6.")
148 parser.add_option('--savetopominutes',
149 dest='savetopominutes',
150 default=45,
151 type='int',
152 help="When cycling, save the topology this number of minutes.")
153 parser.add_option('--showroute',
154 dest='showroute',
155 default='',
156 help="Show the route from the collector to the end point.")
157
158 - def postStartup(self):
159 daemon = zope.component.getUtility(ICollector)
160
161 daemon.network = NetworkModel()
162 daemon.ipv6network = NetworkModel(version=6)
163 if self.options.showroute:
164 targetNode = self.options.showroute
165 if daemon.network.resolveName(targetNode):
166 daemon.network.showRoute(targetNode)
167
168 elif daemon.ipv6network.resolveName(targetNode):
169 daemon.ipv6network.showRoute(targetNode)
170 else:
171 log.error("The IP address %s does not exist in IPv4 or IPv6 topologies.",
172 targetNode)
173 sys.exit(0)
174
175
176 self._getPinger()
177
178
179 task = TracerouteTask(TOPOLOGY_MODELER_NAME + ' IPv4',
180 configId=TOPOLOGY_MODELER_NAME,
181 taskConfig=daemon._prefs)
182 daemon._scheduler.addTask(task, now=True)
183
184 task = TracerouteTask(TOPOLOGY_MODELER_NAME + ' IPv6',
185 configId=TOPOLOGY_MODELER_NAME,
186 taskConfig=daemon._prefs)
187 daemon._scheduler.addTask(task, now=True)
188
189 self.addCorrelatorTasks(daemon)
190
209
216
237
238
244
245
247 """
248 This updates the link between a device and the topology.
249 """
250 zope.interface.implements(IConfigurationListener)
251
252 - def deleted(self, configurationId):
253 """
254 Called when a configuration is deleted from the collector
255 If the device was down and we don't remove from topology,
256 we will send out a bogus 'device down' event.
257 """
258 daemon = zope.component.getUtility(ICollector)
259 if daemon.network.topology is None:
260 return
261
262 tasks = daemon._scheduler.getTasksForConfig(configurationId)
263 if not tasks:
264 log.debug("No tasks found to delete for device '%s'",
265 configurationId)
266 for task in tasks:
267 ipAddress = task._manageIp
268 if ipAddress in daemon.network.topology:
269 daemon.network.removeDevice(ipAddress)
270
271 elif ipAddress in daemon.ipv6network.topology:
272 daemon.ipv6network.removeDevice(ipAddress)
273
274 else:
275 log.debug("%s IP address %s not in topology",
276 configurationId, ipAddress)
277
278 - def added(self, configuration):
279 """
280 Called when a configuration is added to the collector.
281 This links the schedulable tasks to the topology so that we can
282 check on the status of the network devices.
283 """
284 log.debug("zenhub asked us to add device: %s (%s)",
285 configuration.id, configuration.manageIp)
286
287 - def updated(self, newConfiguration):
288 """
289 Called when a configuration is updated in collector
290 """
291 log.debug("zenhub asked us to update device: %s (%s)",
292 newConfiguration.id, newConfiguration.manageIp)
293 pass
294
295
297 try:
298 return gethostbyname(getfqdn())
299 except gaierror:
300
301 ifconfigs = ['/sbin/ifconfig',
302 '/usr/sbin/ifconfig',
303 '/usr/bin/ifconfig',
304 '/bin/ifconfig']
305 ifconfig = filter(os.path.exists, ifconfigs)[0]
306 fp = os.popen(ifconfig + ' -a')
307 config = fp.read().split('\n\n')
308 fp.close()
309 digits = r'[0-9]{1,3}'
310 pat = r'(addr:|inet) *(%s\.%s\.%s\.%s)[^0-9]' % ((digits,)*4)
311 parse = re.compile(pat)
312 results = []
313 for c in config:
314 addr = parse.search(c)
315 if addr:
316 results.append(addr.group(2))
317 try:
318 results.remove('127.0.0.1')
319 except ValueError:
320 pass
321 if results:
322 return results[0]
323 return '127.0.0.1'
324
325
326 if __name__=='__main__':
327 myPreferences = PingCollectionPreferences()
328 myTaskFactory = SimpleTaskFactory(PingCollectionTask)
329 myTaskSplitter = PerIpAddressTaskSplitter(myTaskFactory)
330 myListener = TopologyUpdater()
331 daemon = CollectorDaemon(myPreferences, myTaskSplitter,
332 configurationLister=myListener,
333 stoppingCallback=myPreferences.preShutdown)
334 daemon.run()
335