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