Package Products :: Package ZenModel :: Module PerformanceConf
[hide private]
[frames] | no frames]

Source Code for Module Products.ZenModel.PerformanceConf

  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   
32 - def urlsafe_b64encode(s):
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
73 -def performancePath(target):
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
90 -def manage_addPerformanceConf(context, id, title=None, REQUEST=None,):
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
117 -class PerformanceConf(Monitor, StatusColor):
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 wmibatchSize = 10 135 wmiqueryTimeout = 100 136 configCycleInterval = 6 * 60 137 138 zenProcessParallelJobs = 10 139 140 pingTimeOut = 1.5 141 pingTries = 2 142 pingChunk = 75 143 pingCycleInterval = 60 144 maxPingFailures = 1440 145 146 modelerCycleInterval = 720 147 148 renderurl = '/zport/RenderServer' 149 renderuser = '' 150 renderpass = '' 151 152 discoveryNetworks = () 153 154 # make the default rrdfile size smaller 155 # we need the space to live within the disk cache 156 defaultRRDCreateCommand = ( 157 'RRA:AVERAGE:0.5:1:600', # every 5 mins for 2 days 158 'RRA:AVERAGE:0.5:6:600', # every 30 mins for 12 days 159 'RRA:AVERAGE:0.5:24:600', # every 2 hours for 50 days 160 'RRA:AVERAGE:0.5:288:600', # every day for 600 days 161 'RRA:MAX:0.5:6:600', 162 'RRA:MAX:0.5:24:600', 163 'RRA:MAX:0.5:288:600', 164 ) 165 166 _properties = ( 167 {'id': 'eventlogCycleInterval', 'type': 'int', 'mode': 'w'}, 168 {'id': 'perfsnmpCycleInterval', 'type': 'int', 'mode': 'w'}, 169 {'id': 'processCycleInterval', 'type': 'int', 'mode': 'w'}, 170 {'id': 'statusCycleInterval', 'type': 'int', 'mode': 'w'}, 171 {'id': 'winCycleInterval', 'type': 'int', 'mode': 'w'}, 172 {'id': 'winmodelerCycleInterval', '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' : 'viewHistory' 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
246 - def findDevice(self, deviceName):
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
260 - def getNetworkRoot(self):
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
270 - def buildGraphUrlFromCommands(self, gopts, drange):
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
300 - def performanceGraphUrl(self, context, targetpath, targettype, view, drange):
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
323 - def performanceMGraphUrl(self, context, targetsmap, view, drange):
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
354 - def renderCustomUrl(self, gopts, drange):
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
376 - def performanceCustomSummary(self, gopts):
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
398 - def fetchValues(self, paths, cf, resolution, start, end=""):
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
427 - def currentValues(self, paths):
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
450 - def _fullPerformancePath(self, gopts):
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')
472 - def performanceDeviceList(self, force=True):
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')
491 - def performanceDataSources(self):
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
515 - def deleteRRDFiles(self, device, datasource=None, datapoint=None):
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
541 - def setPerformanceMonitor(self, performanceMonitor=None, deviceNames=None, REQUEST=None):
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')
579 - def getPingDevices(self):
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 _executeZenDiscCommand(self, deviceName, devicePath= "/Discovered", 594 performanceMonitor="localhost", discoverProto="snmp", 595 zSnmpPort=161,zSnmpCommunity="", REQUEST=None):
596 """ 597 Execute zendisc on the new device and return result 598 599 @param deviceName: Name of a device 600 @type deviceName: string 601 @param devicePath: DMD path to create the new device in 602 @type devicePath: string 603 @param performanceMonitor: DMD object that collects from a device 604 @type performanceMonitor: DMD object 605 @param discoverProto: auto or none 606 @type discoverProto: string 607 @param zSnmpPort: zSnmpPort 608 @type zSnmpPort: string 609 @param zSnmpCommunity: SNMP community string 610 @type zSnmpCommunity: string 611 @param REQUEST: Zope REQUEST object 612 @type REQUEST: Zope REQUEST object 613 @return: 614 @rtype: 615 """ 616 zm = binPath('zendisc') 617 zendiscCmd = [zm] 618 zendiscOptions = ['run', '--now','-d', deviceName, 619 '--monitor', performanceMonitor, 620 '--deviceclass', devicePath] 621 if REQUEST: 622 zendiscOptions.append("--weblog") 623 zendiscCmd.extend(zendiscOptions) 624 result = executeCommand(zendiscCmd, REQUEST) 625 return result
626
627 - def executeCollectorCommand(self, command, args, REQUEST=None):
628 """ 629 Executes the collector based daemon command. 630 631 @param command: the collector daemon to run, should not include path 632 @type command: string 633 @param args: list of arguments for the command 634 @type args: list of strings 635 @param REQUEST: Zope REQUEST object 636 @type REQUEST: Zope REQUEST object 637 @return: result of the command 638 @rtype: string 639 """ 640 cmd = binPath(command) 641 daemonCmd = [cmd] 642 daemonCmd.extend(args) 643 result = executeCommand(daemonCmd, REQUEST) 644 return result
645 646
647 - def collectDevice(self, device=None, setlog=True, REQUEST=None, 648 generateEvents=False):
649 """ 650 Collect the configuration of this device AKA Model Device 651 652 @permission: ZEN_MANAGE_DEVICE 653 @param device: Name of a device or entry in DMD 654 @type device: string 655 @param setlog: If true, set up the output log of this process 656 @type setlog: boolean 657 @param REQUEST: Zope REQUEST object 658 @type REQUEST: Zope REQUEST object 659 @param generateEvents: unused 660 @type generateEvents: string 661 """ 662 xmlrpc = isXmlRpc(REQUEST) 663 if setlog and REQUEST and not xmlrpc: 664 handler = setupLoggingHeader(device, REQUEST) 665 666 zenmodelerOpts = ['run', '--now', '--monitor', self.id, '-F', '-d', device.id] 667 if REQUEST: 668 zenmodelerOpts.append('--weblog') 669 result = self._executeZenModelerCommand(zenmodelerOpts, REQUEST) 670 if result and xmlrpc: 671 return result 672 log.info('configuration collected') 673 674 if setlog and REQUEST and not xmlrpc: 675 clearWebLoggingStream(handler) 676 677 if xmlrpc: 678 return 0
679
680 - def _executeZenModelerCommand(self, zenmodelerOpts, REQUEST=None):
681 """ 682 Execute zenmodeler and return result 683 684 @param zenmodelerOpts: zenmodeler command-line options 685 @type zenmodelerOpts: string 686 @param REQUEST: Zope REQUEST object 687 @type REQUEST: Zope REQUEST object 688 @return: results of command 689 @rtype: string 690 """ 691 zm = binPath('zenmodeler') 692 zenmodelerCmd = [zm] 693 zenmodelerCmd.extend(zenmodelerOpts) 694 result = executeCommand(zenmodelerCmd, REQUEST) 695 return result
696 697 698 InitializeClass(PerformanceConf) 699