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 3133 """ 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 s48 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 7476 """ 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 9193 """ 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 118120 """ 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')739 740 741 InitializeClass(PerformanceConf) 742231 """ 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 245247 """ 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 259261 """ 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.Networks268 269271 """ 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 url298 299301 """ 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 322324 """ 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 url352 353355 """ 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 url374 375377 """ 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 397399 """ 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 426428 """ 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 449451 """ 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 gopts469 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 devlist488 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)514516 """ 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 540542 """ 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 devices592593 - 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 jobStatus623624 - 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 result652655 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 zendiscCmd666 669671 """ 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 result688 689690 - 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 0716 717718 - 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
Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Wed Jul 14 12:01:47 2010 | http://epydoc.sourceforge.net |