| Trees | Indices | Help |
|
|---|
|
|
1 #! /usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # ##########################################################################
4 #
5 # This program is part of Zenoss Core, an open source monitoring platform.
6 # Copyright (C) 2006-2009 Zenoss Inc.
7 #
8 # This program is free software; you can redistribute it and/or modify it
9 # under the terms of the GNU General Public License version 2 or (at your
10 # option) any later version as published by the Free Software Foundation.
11 #
12 # For complete information please visit: http://www.zenoss.com/oss/
13 #
14 # ##########################################################################
15
16 __doc__ = """PerformanceConf
17 The configuration object for Performance servers
18 """
19
20 import os
21 import zlib
22 import socket
23 from urllib import urlencode
24 from ipaddr import IPAddress
25 import logging
26 log = logging.getLogger('zen.PerformanceConf')
27
28 from Products.ZenUtils.IpUtil import ipwrap
29
30 try:
31 from base64 import urlsafe_b64encode
32 raise ImportError
33 except ImportError:
34
35
37 """
38 Encode a string so that it's okay to be used in an URL
39
40 @param s: possibly unsafe string passed in by the user
41 @type s: string
42 @return: sanitized, url-safe version of the string
43 @rtype: string
44 """
45
46 import base64
47 s = base64.encodestring(s)
48 s = s.replace('+', '-')
49 s = s.replace('/', '_')
50 s = s.replace('\n', '')
51 return s
52
53
54 import xmlrpclib
55
56 from AccessControl import ClassSecurityInfo
57 from AccessControl import Permissions as permissions
58 from Globals import DTMLFile
59 from Globals import InitializeClass
60 from Monitor import Monitor
61 from Products.PythonScripts.standard import url_quote
62 from Products.Jobber.jobs import ShellCommandJob
63 from Products.ZenModel.ZenossSecurity import *
64 from Products.ZenRelations.RelSchema import *
65 from Products.ZenUtils.Utils import basicAuthUrl, zenPath, binPath
66 from Products.ZenUtils.Utils import unused
67 from Products.ZenUtils.Utils import isXmlRpc
68 from Products.ZenUtils.Utils import setupLoggingHeader
69 from Products.ZenUtils.Utils import executeCommand
70 from Products.ZenUtils.Utils import clearWebLoggingStream
71 from Products.ZenModel.Device import manage_createDevice
72 from Products.ZenModel.ZDeviceLoader import DeviceCreationJob
73 from Products.ZenWidgets import messaging
74 from StatusColor import StatusColor
75
76 PERF_ROOT = None
77
78
80 """
81 Return the base directory where RRD performance files are kept.
82
83 @param target: path to performance file
84 @type target: string
85 @return: sanitized path to performance file
86 @rtype: string
87 """
88 global PERF_ROOT
89 if PERF_ROOT is None:
90 PERF_ROOT = zenPath('perf')
91 if target.startswith('/'):
92 target = target[1:]
93 return os.path.join(PERF_ROOT, target)
94
95
97 """
98 Make a device class
99
100 @param context: Where you are in the Zope acquisition path
101 @type context: Zope context object
102 @param id: unique identifier
103 @type id: string
104 @param title: user readable label (unused)
105 @type title: string
106 @param REQUEST: Zope REQUEST object
107 @type REQUEST: Zope REQUEST object
108 @return:
109 @rtype:
110 """
111 unused(title)
112 dc = PerformanceConf(id)
113 context._setObject(id, dc)
114
115 if REQUEST is not None:
116 REQUEST['RESPONSE'].redirect(context.absolute_url()
117 + '/manage_main')
118
119
120 addPerformanceConf = DTMLFile('dtml/addPerformanceConf', globals())
121
122
124 """
125 Configuration for Performance servers
126 """
127 portal_type = meta_type = 'PerformanceConf'
128
129 monitorRootName = 'Performance'
130
131 security = ClassSecurityInfo()
132 security.setDefaultAccess('allow')
133
134 eventlogCycleInterval = 60
135 perfsnmpCycleInterval = 300
136 processCycleInterval = 180
137 statusCycleInterval = 60
138 winCycleInterval = 60
139 wmibatchSize = 10
140 wmiqueryTimeout = 100
141 configCycleInterval = 6 * 60
142
143 zenProcessParallelJobs = 10
144
145 pingTimeOut = 1.5
146 pingTries = 2
147 pingChunk = 75
148 pingCycleInterval = 60
149 maxPingFailures = 1440
150
151 modelerCycleInterval = 720
152
153 renderurl = '/zport/RenderServer'
154 renderuser = ''
155 renderpass = ''
156
157 discoveryNetworks = ()
158
159 # make the default rrdfile size smaller
160 # we need the space to live within the disk cache
161 defaultRRDCreateCommand = (
162 'RRA:AVERAGE:0.5:1:600', # every 5 mins for 2 days
163 'RRA:AVERAGE:0.5:6:600', # every 30 mins for 12 days
164 'RRA:AVERAGE:0.5:24:600', # every 2 hours for 50 days
165 'RRA:AVERAGE:0.5:288:600', # every day for 600 days
166 'RRA:MAX:0.5:6:600',
167 'RRA:MAX:0.5:24:600',
168 'RRA:MAX:0.5:288:600',
169 )
170
171 _properties = (
172 {'id': 'eventlogCycleInterval', 'type': 'int', 'mode': 'w'},
173 {'id': 'perfsnmpCycleInterval', 'type': 'int', 'mode': 'w'},
174 {'id': 'processCycleInterval', 'type': 'int', 'mode': 'w'},
175 {'id': 'statusCycleInterval', 'type': 'int', 'mode': 'w'},
176 {'id': 'winCycleInterval', 'type': 'int', 'mode': 'w'},
177 {'id': 'wmibatchSize', 'type': 'int', 'mode': 'w',
178 'description':"Number of data objects to retrieve in a single WMI query",},
179 {'id': 'wmiqueryTimeout', 'type': 'int', 'mode': 'w',
180 'description':"Number of milliseconds to wait for WMI query to respond",},
181 {'id': 'configCycleInterval', 'type': 'int', 'mode': 'w'},
182 {'id': 'renderurl', 'type': 'string', 'mode': 'w'},
183 {'id': 'renderuser', 'type': 'string', 'mode': 'w'},
184 {'id': 'renderpass', 'type': 'string', 'mode': 'w'},
185 {'id': 'defaultRRDCreateCommand', 'type': 'lines', 'mode': 'w'
186 },
187 {'id': 'zenProcessParallelJobs', 'type': 'int', 'mode': 'w'},
188 {'id': 'pingTimeOut', 'type': 'float', 'mode': 'w'},
189 {'id': 'pingTries', 'type': 'int', 'mode': 'w'},
190 {'id': 'pingChunk', 'type': 'int', 'mode': 'w'},
191 {'id': 'pingCycleInterval', 'type': 'int', 'mode': 'w'},
192 {'id': 'maxPingFailures', 'type': 'int', 'mode': 'w'},
193 {'id': 'modelerCycleInterval', 'type': 'int', 'mode': 'w'},
194 {'id': 'discoveryNetworks', 'type': 'lines', 'mode': 'w'},
195 )
196
197 _relations = Monitor._relations + (
198 ("devices", ToMany(ToOne,"Products.ZenModel.Device","perfServer")),
199 )
200
201 # Screen action bindings (and tab definitions)
202 factory_type_information = (
203 {
204 'immediate_view' : 'viewPerformanceConfOverview',
205 'actions' :
206 (
207 { 'id' : 'overview'
208 , 'name' : 'Overview'
209 , 'action' : 'viewPerformanceConfOverview'
210 , 'permissions' : (
211 permissions.view, )
212 },
213 { 'id' : 'edit'
214 , 'name' : 'Edit'
215 , 'action' : 'editPerformanceConf'
216 , 'permissions' : ("Manage DMD",)
217 },
218 { 'id' : 'performance'
219 , 'name' : 'Performance'
220 , 'action' : 'viewDaemonPerformance'
221 , 'permissions' : (permissions.view,)
222 },
223 { 'id' : 'viewHistory'
224 , 'name' : 'Modifications'
225 , 'action' : 'viewNewHistory'
226 , 'permissions' : (ZEN_VIEW_MODIFICATIONS,)
227 },
228 )
229 },
230 )
231
232
233 security.declareProtected('View', 'getDefaultRRDCreateCommand')
235 """
236 Get the default RRD Create Command, as a string.
237 For example:
238 '''RRA:AVERAGE:0.5:1:600
239 RRA:AVERAGE:0.5:6:600
240 RRA:AVERAGE:0.5:24:600
241 RRA:AVERAGE:0.5:288:600
242 RRA:MAX:0.5:288:600'''
243
244 @return: RRD create command
245 @rtype: string
246 """
247 return '\n'.join(self.defaultRRDCreateCommand)
248
249
251 """
252 Return the object given the name
253
254 @param deviceName: Name of a device
255 @type deviceName: string
256 @return: device corresponding to the name, or None
257 @rtype: device object
258 """
259 brains = self.dmd.Devices._findDevice(deviceName)
260 if brains:
261 return brains[0].getObject()
262
263
265 """
266 Get the root of the Network object in the DMD
267
268 @return: base DMD Network object
269 @rtype: Network object
270 """
271 return self.dmd.Networks.getNetworkRoot(version)
272
273
275 """
276 Return an URL for the given graph options and date range
277
278 @param gopts: graph options
279 @type gopts: string
280 @param drange: time range to use
281 @type drange: string
282 @return: URL to a graphic
283 @rtype: string
284 """
285 newOpts = []
286 width = 0
287 for o in gopts:
288 if o.startswith('--width'):
289 width = o.split('=')[1].strip()
290 continue
291 newOpts.append(o)
292
293 encodedOpts = urlsafe_b64encode(
294 zlib.compress('|'.join(newOpts), 9))
295 params = {
296 'gopts': encodedOpts,
297 'drange': drange,
298 'width': width,
299 }
300 if self.renderurl.startswith('proxy'):
301 url = url.replace('proxy', 'http', 1)
302 params['remoteUrl'] = url
303 return '/zport/RenderServer/render?%s' % (urlencode(params),)
304 else:
305 return '%s/render?%s' % (self.renderurl, urlencode(params),)
306
307
309 """
310 Set the full path of the target and send to view
311
312 @param context: Where you are in the Zope acquisition path
313 @type context: Zope context object
314 @param targetpath: device path of performance metric
315 @type targetpath: string
316 @param targettype: unused
317 @type targettype: string
318 @param view: view object
319 @type view: Zope object
320 @param drange: date range
321 @type drange: string
322 @return: URL to graph
323 @rtype: string
324 """
325 unused(targettype)
326 targetpath = performancePath(targetpath)
327 gopts = view.getGraphCmds(context, targetpath)
328 return self.buildGraphUrlFromCommands(gopts, drange)
329
330
332 """
333 Set the full paths for all targts in map and send to view
334
335 @param context: Where you are in the Zope acquisition path
336 @type context: Zope context object
337 @param targetsmap: list of (target, targettype) tuples
338 @type targetsmap: list
339 @param view: view object
340 @type view: Zope object
341 @param drange: date range
342 @type drange: string
343 @return: URL to graph
344 @rtype: string
345 """
346 ntm = []
347 for (target, targettype) in targetsmap:
348 if target.find('.rrd') == -1:
349 target += '.rrd'
350 fulltarget = performancePath(target)
351 ntm.append((fulltarget, targettype))
352 gopts = view.multiGraphOpts(context, ntm)
353 gopts = url_quote('|'.join(gopts))
354 url = '%s/render?gopts=%s&drange=%d' % (self.renderurl, gopts, drange)
355 if self.renderurl.startswith('http'):
356 return '/zport/RenderServer/render?remoteUrl=%s&gopts=%s&drange=%d' % (
357 url_quote(url), gopts, drange)
358 else:
359 return url
360
361
363 """
364 Return the URL for a list of custom gopts for a graph
365
366 @param gopts: graph options
367 @type gopts: string
368 @param drange: date range
369 @type drange: string
370 @return: URL to graph
371 @rtype: string
372 """
373 gopts = self._fullPerformancePath(gopts)
374 gopts = url_quote('|'.join(gopts))
375 url = '%s/render?gopts=%s&drange=%d' % (self.renderurl, gopts,
376 drange)
377 if self.renderurl.startswith('http'):
378 return '/zport/RenderServer/render?remoteUrl=%s&gopts=%s&drange=%d'\
379 % (url_quote(url), gopts, drange)
380 else:
381 return url
382
383
385 """
386 Fill out full path for custom gopts and call to server
387
388 @param gopts: graph options
389 @type gopts: string
390 @return: URL
391 @rtype: string
392 """
393 gopts = self._fullPerformancePath(gopts)
394 renderurl = str(self.renderurl)
395 if renderurl.startswith('proxy'):
396 renderurl = self.renderurl.replace('proxy', 'http')
397 if renderurl.startswith('http'):
398 url = basicAuthUrl(str(self.renderuser),
399 str(self.renderpass), renderurl)
400 server = xmlrpclib.Server(url)
401 else:
402 server = self.getObjByPath(renderurl)
403 return server.summary(gopts)
404
405
407 """
408 Return values
409
410 @param paths: paths to performance metrics
411 @type paths: list
412 @param cf: RRD CF
413 @type cf: string
414 @param resolution: resolution
415 @type resolution: string
416 @param start: start time
417 @type start: string
418 @param end: end time
419 @type end: string
420 @return: values
421 @rtype: list
422 """
423 url = self.renderurl
424 if url.startswith("http"):
425 url = basicAuthUrl(self.renderuser, self.renderpass, self.renderurl)
426 server = xmlrpclib.Server(url, allow_none=True)
427 else:
428 if not self.renderurl:
429 raise KeyError
430 server = self.getObjByPath(self.renderurl)
431 return server.fetchValues(map(performancePath, paths), cf,
432 resolution, start, end)
433
434
436 """
437 Fill out full path and call to server
438
439 @param paths: paths to performance metrics
440 @type paths: list
441 @return: values
442 @rtype: list
443 """
444 url = self.renderurl
445 if url.startswith('proxy'):
446 url = self.renderurl.replace('proxy', 'http')
447 if url.startswith('http'):
448 url = basicAuthUrl(self.renderuser, self.renderpass,
449 self.renderurl)
450 server = xmlrpclib.Server(url)
451 else:
452 if not self.renderurl:
453 raise KeyError
454 server = self.getObjByPath(self.renderurl)
455 return server.currentValues(map(performancePath, paths))
456
457
459 """
460 Add full path to a list of custom graph options
461
462 @param gopts: graph options
463 @type gopts: string
464 @return: full path + graph options
465 @rtype: string
466 """
467 for i in range(len(gopts)):
468 opt = gopts[i]
469 if opt.find('DEF') == 0:
470 opt = opt.split(':')
471 (var, file) = opt[1].split('=')
472 file = performancePath(file)
473 opt[1] = '%s=%s' % (var, file)
474 opt = ':'.join(opt)
475 gopts[i] = opt
476 return gopts
477
478
479 security.declareProtected('View', 'performanceDeviceList')
481 """
482 Return a list of URLs that point to our managed devices
483
484 @param force: unused
485 @type force: boolean
486 @return: list of device objects
487 @rtype: list
488 """
489 unused(force)
490 devlist = []
491 for dev in self.devices():
492 dev = dev.primaryAq()
493 if not dev.pastSnmpMaxFailures() and dev.monitorDevice():
494 devlist.append(dev.getPrimaryUrlPath(full=True))
495 return devlist
496
497
498 security.declareProtected('View', 'performanceDataSources')
500 """
501 Return a string that has all the definitions for the performance DS's.
502
503 @return: list of Data Sources
504 @rtype: string
505 """
506 dses = []
507 oidtmpl = 'OID %s %s'
508 dstmpl = """datasource %s
509 rrd-ds-type = %s
510 ds-source = snmp://%%snmp%%/%s%s
511 """
512 rrdconfig = self.getDmdRoot('Devices').rrdconfig
513 for ds in rrdconfig.objectValues(spec='RRDDataSource'):
514 if ds.isrow:
515 inst = '.%inst%'
516 else:
517 inst = ''
518 dses.append(oidtmpl % (ds.getName(), ds.oid))
519 dses.append(dstmpl % (ds.getName(), ds.rrdtype,
520 ds.getName(), inst))
521 return '\n'.join(dses)
522
524 """
525 Remove RRD performance data files
526
527 @param device: Name of a device or entry in DMD
528 @type device: string
529 @param datasource: datasource name
530 @type datasource: string
531 @param datapoint: datapoint name
532 @type datapoint: string
533 """
534 remoteUrl = None
535 if self.renderurl.startswith('http'):
536 if datapoint:
537 remoteUrl = '%s/deleteRRDFiles?device=%s&datapoint=%s' % (
538 self.renderurl, device, datapoint)
539 elif datasource:
540 remoteUrl = '%s/deleteRRDFiles?device=%s&datasource=%s' % (
541 self.renderurl, device, datasource)
542 else:
543 remoteUrl = '%s/deleteRRDFiles?device=%s' % (
544 self.renderurl, device)
545 rs = self.getDmd().getParentNode().RenderServer
546 rs.deleteRRDFiles(device, datasource, datapoint, remoteUrl)
547
548
550 """
551 Provide a method to set performance monitor from any organizer
552
553 @param performanceMonitor: DMD object that collects from a device
554 @type performanceMonitor: DMD object
555 @param deviceNames: list of device names
556 @type deviceNames: list
557 @param REQUEST: Zope REQUEST object
558 @type REQUEST: Zope REQUEST object
559 """
560 if not performanceMonitor:
561 if REQUEST:
562 messaging.IMessageSender(self).sendToBrowser('Error',
563 'No monitor was selected.',
564 priority=messaging.WARNING)
565 return self.callZenScreen(REQUEST)
566 if deviceNames is None:
567 if REQUEST:
568 messaging.IMessageSender(self).sendToBrowser('Error',
569 'No devices were selected.',
570 priority=messaging.WARNING)
571 return self.callZenScreen(REQUEST)
572 for devName in deviceNames:
573 dev = self.devices._getOb(devName)
574 dev = dev.primaryAq()
575 dev.setPerformanceMonitor(performanceMonitor)
576 if REQUEST:
577 messaging.IMessageSender(self).sendToBrowser('Monitor Set',
578 'Performance monitor was set to %s.'
579 % performanceMonitor)
580 if REQUEST.has_key('oneKeyValueSoInstanceIsntEmptyAndEvalToFalse'):
581 return REQUEST['message']
582 else:
583 return self.callZenScreen(REQUEST)
584
585
586 security.declareProtected('View', 'getPingDevices')
588 """
589 Return devices associated with this monitor configuration.
590
591 @return: list of devices for this monitor
592 @rtype: list
593 """
594 devices = []
595 for dev in self.devices.objectValuesAll():
596 dev = dev.primaryAq()
597 if dev.monitorDevice() and not dev.zPingMonitorIgnore:
598 devices.append(dev)
599 return devices
600
601 - def addDeviceCreationJob(self, deviceName, devicePath, title=None,
602 discoverProto="none",
603 performanceMonitor='localhost',
604 rackSlot=0, productionState=1000, comments="",
605 hwManufacturer="", hwProductName="",
606 osManufacturer="", osProductName="", priority = 3,
607 locationPath="", systemPaths=[], groupPaths=[],
608 tag="", serialNumber="", zProperties={}):
609
610 # Check to see if we got passed in an IPv6 address
611 try:
612 ipv6addr = IPAddress(deviceName)
613 if not title:
614 title = deviceName
615 deviceName = ipwrap(deviceName)
616 except ValueError:
617 pass
618
619 zendiscCmd = self._getZenDiscCommand(deviceName, devicePath,
620 performanceMonitor)
621
622 jobStatus = self.dmd.JobManager.addJob(DeviceCreationJob,
623 deviceName=deviceName,
624 devicePath=devicePath,
625 title=title,
626 discoverProto=discoverProto,
627 performanceMonitor=performanceMonitor,
628 rackSlot=rackSlot,
629 productionState=productionState,
630 comments=comments,
631 hwManufacturer=hwManufacturer,
632 hwProductName=hwProductName,
633 osManufacturer=osManufacturer,
634 osProductName=osProductName,
635 priority=priority,
636 tag=tag,
637 serialNumber=serialNumber,
638 locationPath=locationPath,
639 systemPaths=systemPaths,
640 groupPaths=groupPaths,
641 zProperties=zProperties,
642 zendiscCmd=zendiscCmd)
643 return jobStatus
644
645 - def _executeZenDiscCommand(self, deviceName, devicePath= "/Discovered",
646 performanceMonitor="localhost",
647 background=False, REQUEST=None):
648 """
649 Execute zendisc on the new device and return result
650
651 @param deviceName: Name of a device
652 @type deviceName: string
653 @param devicePath: DMD path to create the new device in
654 @type devicePath: string
655 @param performanceMonitor: DMD object that collects from a device
656 @type performanceMonitor: DMD object
657 @param background: should command be scheduled job?
658 @type background: boolean
659 @param REQUEST: Zope REQUEST object
660 @type REQUEST: Zope REQUEST object
661 @return:
662 @rtype:
663 """
664 zendiscCmd = self._getZenDiscCommand(deviceName, devicePath,
665 performanceMonitor, REQUEST)
666 if background:
667 log.info('queued job: %s', " ".join(zendiscCmd))
668 result = self.dmd.JobManager.addJob(ShellCommandJob,
669 zendiscCmd)
670 else:
671 result = executeCommand(zendiscCmd, REQUEST)
672 return result
673
676
677 zm = binPath('zendisc')
678 zendiscCmd = [zm]
679 zendiscOptions = ['run', '--now','-d', deviceName,
680 '--monitor', performanceMonitor,
681 '--deviceclass', devicePath]
682 if REQUEST:
683 zendiscOptions.append("--weblog")
684 zendiscCmd.extend(zendiscOptions)
685 log.info('local zendiscCmd is "%s"' % ' '.join(zendiscCmd))
686 return zendiscCmd
687
690
692 """
693 Executes the collector based daemon command.
694
695 @param command: the collector daemon to run, should not include path
696 @type command: string
697 @param args: list of arguments for the command
698 @type args: list of strings
699 @param REQUEST: Zope REQUEST object
700 @type REQUEST: Zope REQUEST object
701 @return: result of the command
702 @rtype: string
703 """
704 cmd = binPath(command)
705 daemonCmd = [cmd]
706 daemonCmd.extend(args)
707 result = executeCommand(daemonCmd, REQUEST)
708 return result
709
710
711 - def collectDevice(self, device=None, setlog=True, REQUEST=None,
712 generateEvents=False, background=False, write=None):
713 """
714 Collect the configuration of this device AKA Model Device
715
716 @permission: ZEN_MANAGE_DEVICE
717 @param device: Name of a device or entry in DMD
718 @type device: string
719 @param setlog: If true, set up the output log of this process
720 @type setlog: boolean
721 @param REQUEST: Zope REQUEST object
722 @type REQUEST: Zope REQUEST object
723 @param generateEvents: unused
724 @type generateEvents: string
725 """
726 xmlrpc = isXmlRpc(REQUEST)
727 zenmodelerOpts = ['run', '--now', '--monitor', self.id,
728 '-F', '-d', device.id]
729 result = self._executeZenModelerCommand(zenmodelerOpts, background,
730 REQUEST, write)
731 if result and xmlrpc:
732 return result
733 log.info('configuration collected')
734
735 if xmlrpc:
736 return 0
737
738
739 - def _executeZenModelerCommand(self, zenmodelerOpts, background=False,
740 REQUEST=None, write=None):
741 """
742 Execute zenmodeler and return result
743
744 @param zenmodelerOpts: zenmodeler command-line options
745 @type zenmodelerOpts: string
746 @param REQUEST: Zope REQUEST object
747 @type REQUEST: Zope REQUEST object
748 @return: results of command
749 @rtype: string
750 """
751 zm = binPath('zenmodeler')
752 zenmodelerCmd = [zm]
753 zenmodelerCmd.extend(zenmodelerOpts)
754 if background:
755 log.info('queued job: %s', " ".join(zenmodelerCmd))
756 result = self.dmd.JobManager.addJob(ShellCommandJob,zenmodelerCmd)
757 else:
758 result = executeCommand(zenmodelerCmd, REQUEST, write)
759 return result
760
761
762 InitializeClass(PerformanceConf)
763
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1.1812 on Thu Sep 1 19:03:47 2011 | http://epydoc.sourceforge.net |