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