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