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

Source Code for Module 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) 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 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
243 - def findDevice(self, deviceName):
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
257 - def getNetworkRoot(self):
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
267 - def buildGraphUrlFromCommands(self, gopts, drange):
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
297 - def performanceGraphUrl(self, context, targetpath, targettype, view, drange):
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
320 - def performanceMGraphUrl(self, context, targetsmap, view, drange):
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
351 - def renderCustomUrl(self, gopts, drange):
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
373 - def performanceCustomSummary(self, gopts):
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
395 - def fetchValues(self, paths, cf, resolution, start, end=""):
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
424 - def currentValues(self, paths):
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
447 - def _fullPerformancePath(self, gopts):
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')
469 - def performanceDeviceList(self, force=True):
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')
488 - def performanceDataSources(self):
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
512 - def deleteRRDFiles(self, device, datasource=None, datapoint=None):
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
538 - def setPerformanceMonitor(self, performanceMonitor=None, deviceNames=None, REQUEST=None):
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')
576 - def getPingDevices(self):
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
624 - def executeCollectorCommand(self, command, args, REQUEST=None):
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
644 - def collectDevice(self, device=None, setlog=True, REQUEST=None, 645 generateEvents=False):
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
677 - def _executeZenModelerCommand(self, zenmodelerOpts, REQUEST=None):
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