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

# Source Code for Module Products.ZenModel.RRDTemplate

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2007, Zenoss Inc.
      5  #
      6  # This program is free software; you can redistribute it and/or modify it
      7  # under the terms of the GNU General Public License version 2 as published by
      8  # the Free Software Foundation.
      9  #
     10  # For complete information please visit: http://www.zenoss.com/oss/
     11  #
     12  ###########################################################################
     13
     14  import sys
     15  from Globals import DTMLFile
     16  from Globals import InitializeClass
     17  from AccessControl import ClassSecurityInfo, Permissions
     18  from Products.ZenModel.ZenossSecurity import *
     19  from zope.interface import implements
     20  from Acquisition import aq_parent
     21  from ZenModelRM import ZenModelRM
     22  from Products.ZenModel.interfaces import IIndexed
     23
     24  from Products.ZenRelations.RelSchema import *
     25  from Products.ZenModel.RRDDataSource import SimpleRRDDataSource
     26  from Products.ZenModel.BasicDataSource import BasicDataSource
     27  from Products.ZenModel.BuiltInDS import BuiltInDS
     28  from Products.ZenModel.ConfigurationError import ConfigurationError
     29  from Products.ZenUtils.Utils import importClass
     30  from Products.ZenWidgets import messaging
     31  from RRDDataPoint import SEPARATOR
     32  from ZenPackable import ZenPackable
     33
     34  import logging
     35  log = logging.getLogger('zen.RRDTemplate')
     36
     37  RRDTEMPLATE_CATALOG = 'searchRRDTemplates'
     38
     39
     40 -def CreateRRDTemplatesCatalog(dmd, rebuild=False):
     41      """
     42      Create the searchRRDTemplates catalog if it does not already exist.
     43      Return the catalog.
     44      """
     45      from Products.ZCatalog.ZCatalog import manage_addZCatalog
     46      from Products.ZenUtils.Search import makeCaseSensitiveFieldIndex, \
     47                                              makePathIndex
     48      zcat = getattr(dmd, RRDTEMPLATE_CATALOG, None)
     49      if zcat and rebuild:
     50          dmd._delObject(RRDTEMPLATE_CATALOG)
     51          zcat = None
     52      if zcat is None:
     53          manage_addZCatalog(dmd, RRDTEMPLATE_CATALOG, RRDTEMPLATE_CATALOG)
     54          zcat = dmd._getOb(RRDTEMPLATE_CATALOG)
     55          cat = zcat._catalog
     56          cat.addIndex('id', makeCaseSensitiveFieldIndex('id'))
     57          cat.addIndex('getPhysicalPath', makePathIndex('getPhysicalPath'))
     58      return zcat
     59
     60
     61 -def YieldAllRRDTemplates(root, criteria=None):
     62      """
     63      Yield all templates in the searchRRDTemplates catalog which fall under
     64      the given root and match the given criteria.  To get all RRDTemplates
     65      pass dmd in as root.  If criteria contains a
     66      value for getPhysicalRoot then the root parameter will be ignored.
     67
     68      If the searchRRDTemplates catalog is not present then fall back to using
     69      DeviceClass.getAllRRDTemplatesPainfully().  In this case root must
     70      be a DeviceClass and criteria is ignored. (This is compatible with
     71      previous DeviceClass.getAllRRDTemplates usage.)
     72
     73      The searchRRDTemplates catalog was added in 2.2
     74      """
     75      zcat = getattr(root, RRDTEMPLATE_CATALOG, None)
     76      if zcat is not None:
     77          criteria = criteria or {}
     78          criteria.setdefault('getPhysicalPath', root.getPrimaryId())
     79          brains = zcat(criteria)
     80          for result in brains:
     81              yield result.getObject()
     82      else:
     83          for t in root.getAllRRDTemplatesPainfully():
     84              yield t
     85
     86
     87 -def manage_addRRDTemplate(context, id, REQUEST = None):
     88      """make a RRDTemplate"""
     89      tt = RRDTemplate(id)
     90      context._setObject(tt.id, tt)
     91      if REQUEST is not None:
     92          REQUEST['RESPONSE'].redirect(context.absolute_url()+'/manage_main')
     93
     94
     95  addRRDTemplate = DTMLFile('dtml/addRRDTemplate',globals())
     96
     97
     98 -def crumbspath(templ, crumbs, idx=-1):
     99      """Create the crumbs path for sub objects of an RRDTemplate.
    100      """
    101      return crumbs
    102
    103
    104 -class RRDTemplate(ZenModelRM, ZenPackable):
    105
    106      implements(IIndexed)
    107      meta_type = 'RRDTemplate'
    108
    109      default_catalog = RRDTEMPLATE_CATALOG
    110
    111      security = ClassSecurityInfo()
    112
    113      description = ""
    114      targetPythonClass = "Products.ZenModel.Device"
    115
    116      _properties = (
    117          {'id':'description', 'type':'text', 'mode':'w'},
    118          {'id':'targetPythonClass', 'type':'string', 'mode':'w'},
    119          )
    120
    121      # The graphs relationship can be removed post 2.1.  It is needed
    122      # by the graphDefinitionAndFriends migrate script for 2.1
    123
    124      _relations =  ZenPackable._relations + (
    125          ("deviceClass", ToOne(
    126              ToManyCont,"Products.ZenModel.TemplateContainer", "rrdTemplates")),
    127          ("datasources", ToManyCont(
    128              ToOne,"Products.ZenModel.RRDDataSource", "rrdTemplate")),
    129          ("graphs", ToManyCont(
    130              ToOne,"Products.ZenModel.RRDGraph", "rrdTemplate")),
    131          ("thresholds", ToManyCont(
    132              ToOne,"Products.ZenModel.ThresholdClass", "rrdTemplate")),
    133          ("graphDefs", ToManyCont(
    134              ToOne,"Products.ZenModel.GraphDefinition", "rrdTemplate")),
    135          )
    136
    137
    138      # Screen action bindings (and tab definitions)
    139      factory_type_information = (
    140      {
    141          'immediate_view' : 'viewRRDTemplate',
    142          'actions'        :
    143          (
    144              { 'id'            : 'overview'
    145              , 'name'          : 'Performance Template'
    146              , 'action'        : 'viewRRDTemplate'
    147              , 'permissions'   : ( Permissions.view, )
    148              },
    149          )
    150      },
    151      )
    152
    153 -    def breadCrumbs(self, terminator='dmd'):
    154          """Return the breadcrumb links for this object add ActionRules list.
    155          [('url','id'), ...]
    156          """
    157          crumbs = super(RRDTemplate, self).breadCrumbs(terminator)
    158          return crumbspath(self, crumbs)
    159
    160
    161 -    def isEditable(self, context):
    162          """Is this template editable in context.
    163          """
    164          return ((context == self or context.isLocalName(self.id))
    165                  and self.checkRemotePerm(ZEN_CHANGE_DEVICE, self))
    166
    167
    168 -    def getGraphDefs(self):
    169          ''' Return an ordered list of the graph definitions
    170          '''
    171          def cmpGraphDefs(a, b):
    172              try: a = int(a.sequence)
    173              except ValueError: a = sys.maxint
    174              try: b = int(b.sequence)
    175              except ValueError: b = sys.maxint
    176              return cmp(a, b)
    177          graphDefs =  [g for g in self.graphDefs()]
    178          graphDefs.sort(cmpGraphDefs)
    179          return graphDefs
    180
    181
    182 -    def getRRDPath(self):
    183          """Return the path on which this template is defined.
    184          """
    185          return self.getPrimaryParent().getPrimaryDmdId(subrel="rrdTemplates")
    186
    187
    188 -    def getGraphableThresholds(self):
    189          ''' Return a list of names of graphable thresholds
    190          '''
    191          return [t for t in self.thresholds()]
    192
    193
    194 -    def getRRDDataPointNames(self):
    195          """Return the list of all datapoint names.
    196          """
    197          # We check for the presence of datapoints on the datasources
    198          # to better handle situation where the datasource is broken
    199          # (usually because of a missing zenpack.)
    200          datasources = [ds for ds in self.datasources()
    201                          if hasattr(ds, 'datapoints')]
    202          return [dp.name() for ds in datasources for dp in ds.datapoints()]
    203
    204
    205 -    def getRRDDataSources(self, dsType=None):
    206          """Return a list of all datapoints on this template.
    207          """
    208          if dsType is None: return self.datasources()
    209          return [ds for ds in self.datasources()
    210                  if ds.sourcetype == dsType
    211                  or (dsType=='COMMAND' and ds.useZenCommand())]
    212
    213
    214 -    def getRRDDataPoints(self):
    215          """Return a list of all datapoints on this template.
    216          """
    217          result = []
    218          for s in self.datasources():
    219              result.extend(s.datapoints())
    220          return result
    221
    222
    223 -    def getRRDDataPoint(self, name):
    224          """Return a datapoint based on its name.
    225          """
    226          source = name
    227          point = name
    228          if name.find(SEPARATOR) >= 0:
    229              source, point = name.split(SEPARATOR, 1)
    230          ds = self.datasources._getOb(source, None)
    231          if ds is None:
    232              results = []
    233              for ds in self.datasources():
    234                  for dp in ds.datapoints():
    235                      if dp.name() == name:
    236                          results.append(dp)
    237              if len(results) == 1:
    238                  return results[0]
    239          else:
    240              return ds.datapoints._getOb(point)
    241          raise ConfigurationError('Unknown data point "%s"' % name)
    242
    243
    244      security.declareProtected('Add DMD Objects', 'manage_addRRDDataSource')
    245 -    def manage_addRRDDataSource(self, id, dsOption, REQUEST=None):
    246          """Add an RRDDataSource to this DeviceClass.
    247          """
    248          ds = None
    249          if id and dsOption:
    250              ds = self.getDataSourceInstance(id, dsOption)
    251              self.datasources._setObject(ds.id, ds)
    252              ds = self.datasources._getOb(ds.id)
    253              if ds:
    254                  ds.addDataPoints()
    255          if REQUEST:
    256              if ds:
    257                  messaging.IMessageSender(self).sendToBrowser(
    258                      'Datasource Added',
    259                      "Data source %s added" % ds.id
    260                  )
    261                  url = '%s/datasources/%s' % (self.getPrimaryUrlPath(), ds.id)
    262                  return REQUEST['RESPONSE'].redirect(url)
    263              else:
    264                  return self.callZenScreen(REQUEST)
    265          return ds
    266
    267
    268 -    def getTargetPythonClass(self):
    269          """
    270          Returns the python class object that this template can be bound to.
    271          """
    272          from Products.ZenModel.Device import Device
    273          cname = getattr(self, "targetPythonClass", None)
    274          if cname:
    275              try:
    276                  return importClass(cname)
    277              except ImportError:
    278                  log.exception("Unable to import class " + cname)
    279          return Device
    280
    281
    282      security.declareProtected(ZEN_MANAGE_DMD, 'manage_deleteRRDDataSources')
    283 -    def manage_deleteRRDDataSources(self, ids=(), REQUEST=None):
    284          """Delete RRDDataSources from this DeviceClass
    285          """
    286          def clean(rel, id):
    287              for obj in rel():
    288                  if id in obj.dsnames:
    289                      obj.dsnames.remove(id)
    290                      if not obj.dsnames:
    291                          rel._delObject(obj.id)
    292
    293          if not ids: return self.callZenScreen(REQUEST)
    294          for id in ids:
    295              self._p_changed = True
    296              if getattr(self.datasources,id,False):
    297                  if getattr(self, 'device', False):
    298                      perfConf = self.device().getPerformanceServer()
    299                      if perfConf:
    300                          perfConf.deleteRRDFiles(device=self.device().id,
    301                                                  datasource=id)
    302                  else:
    303                      for d in self.deviceClass.obj.getSubDevicesGen():
    304                          perfConf = d.getPerformanceServer()
    305                          if perfConf:
    306                              perfConf.deleteRRDFiles(device=d, datasource=id)
    307
    308                  self.datasources._delObject(id)
    309                  clean(self.graphs, id)
    310                  clean(self.thresholds, id)
    311
    312          if REQUEST:
    313              messaging.IMessageSender(self).sendToBrowser(
    314                  'Datasources Deleted',
    315                  'Datasource%s %s deleted.' % ('' if len(ids)==1 else 's',
    316                                                ', '.join(ids))
    317              )
    318              return self.callZenScreen(REQUEST)
    319
    320
    321      security.declareProtected('Add DMD Objects', 'manage_addRRDThreshold')
    322 -    def manage_addRRDThreshold(self, id, thresholdClassName, REQUEST=None):
    323          """Add an RRDThreshold to this DeviceClass.
    324          """
    325          if not id: return self.callZenScreen(REQUEST)
    326          org = self.getThresholdClass(id, thresholdClassName)
    327          self.thresholds._setObject(org.id, org)
    328          org = self.thresholds._getOb(org.id)
    329          if REQUEST:
    330              if org:
    331                  messaging.IMessageSender(self).sendToBrowser(
    332                      'Threshold Added',
    333                      'Threshold "%s" added' % org.id
    334                  )
    335                  url = '%s/thresholds/%s' % (self.getPrimaryUrlPath(), org.id)
    336                  return REQUEST['RESPONSE'].redirect(url)
    337              else:
    338                  return self.callZenScreen(REQUEST)
    339          return org
    340
    341
    342      security.declareProtected(ZEN_MANAGE_DMD, 'manage_deleteRRDThresholds')
    343 -    def manage_deleteRRDThresholds(self, ids=(), REQUEST=None):
    344          """Delete RRDThresholds from this DeviceClass
    345          """
    346          if not ids: return self.callZenScreen(REQUEST)
    347          for id in ids:
    348              if getattr(self.thresholds,id,False):
    349                  self.thresholds._delObject(id)
    350          if REQUEST:
    351              messaging.IMessageSender(self).sendToBrowser(
    352                  'Thresholds Deleted',
    353                  'Threshold%s %s deleted.' % ('' if len(ids)==1 else 's',
    354                                                ', '.join(ids))
    355              )
    356              return self.callZenScreen(REQUEST)
    357
    358
    359      security.declareProtected(ZEN_MANAGE_DMD, 'manage_addGraphDefinition')
    360 -    def manage_addGraphDefinition(self, new_id, REQUEST=None):
    361          """Add a GraphDefinition to our RRDTemplate.
    362          """
    363          from GraphDefinition import GraphDefinition
    364          self.getGraphDefs()
    365          graph = None
    366          graph = GraphDefinition(new_id)
    367          graph.sequence = len(self.graphDefs())
    368          self.graphDefs._setObject(graph.id, graph)
    369          graph = self.graphDefs._getOb(graph.id)
    370          if REQUEST:
    371              messaging.IMessageSender(self).sendToBrowser(
    372                  'Graph Added',
    373                  'Graph "%s" added' % graph.id
    374              )
    375              url = '%s/graphDefs/%s' % (self.getPrimaryUrlPath(), graph.id)
    376              return REQUEST['RESPONSE'].redirect(url)
    377          return graph
    378
    379
    380      security.declareProtected(ZEN_MANAGE_DMD, 'manage_deleteGraphDefinitions')
    381 -    def manage_deleteGraphDefinitions(self, ids=(), REQUEST=None):
    382          """Remove GraphDefinitions from this RRDTemplate.
    383          """
    384          for id in ids:
    385              self.graphDefs._delObject(id)
    386              self.manage_resequenceGraphDefs()
    387          if REQUEST:
    388              messaging.IMessageSender(self).sendToBrowser(
    389                  'Graphs Deleted',
    390                  'Graph%s %s deleted.' % ('' if len(ids)==1 else 's',
    391                                                ', '.join(ids))
    392              )
    393              return self.callZenScreen(REQUEST)
    394
    395
    396      security.declareProtected(ZEN_MANAGE_DMD, 'manage_resequenceGraphDefs')
    397 -    def manage_resequenceGraphDefs(self, seqmap=(), origseq=(), REQUEST=None):
    398          """Reorder the sequence of the GraphDefinitions.
    399          """
    400          from Products.ZenUtils.Utils import resequence
    401          return resequence(self, self.getGraphDefs(),
    402                              seqmap, origseq, REQUEST)
    403
    404
    405      security.declareProtected(ZEN_MANAGE_DMD, 'manage_addDataSourcesToGraphs')
    406 -    def manage_addDataSourcesToGraphs(self, ids=(), graphIds=(), REQUEST=None):
    407          """
    408          Create GraphPoints for all datapoints in the given datasources (ids)
    409          in each of the graphDefs (graphIds.)
    410          If a graphpoint already exists for a datapoint in a graphDef then
    411          don't create a 2nd one.
    412          """
    413          newGraphPoints = []
    414          for dsId in ids:
    415              ds = self.datasources._getOb(dsId, None)
    416              if ds:
    417                  newGraphPoints += ds.manage_addDataPointsToGraphs(
    418                      [dp.id for dp in ds.datapoints()],
    419                      graphIds)
    420          numAdded = len(newGraphPoints)
    421          if REQUEST:
    422              messaging.IMessageSender(self).sendToBrowser(
    423                  'Graph Points Added',
    424                  'Added %s GraphPoint%s' % (numAdded, numAdded != 1 and 's' or '')
    425              )
    426              return self.callZenScreen(REQUEST)
    427          return newGraphPoints
    428
    429
    430      security.declareProtected(ZEN_MANAGE_DMD, 'manage_addDataSourcesToGraphs')
    431 -    def manage_addThresholdsToGraphs(self, ids=(), graphIds=(), REQUEST=None):
    432          """
    433          Create GraphPoints for all given thresholds that are not already
    434          graphed. in the given datasources (ids)
    435          """
    436          newGps = []
    437          for graphId in graphIds:
    438              graphDef = self.graphDefs._getOb(graphId, None)
    439              if graphDef:
    440                  for threshId in ids:
    441                      thresh = self.thresholds._getOb(threshId, None)
    442                      if thresh and not graphDef.isThresholdGraphed(thresh.id):
    443                          newGps += graphDef.manage_addThresholdGraphPoints(
    444                                                                  [thresh.id])
    445          if REQUEST:
    446              numAdded = len(newGps)
    447              messaging.IMessageSender(self).sendToBrowser(
    448                  'Graph Points Added',
    449                  'Added %s GraphPoint%s' % (numAdded, numAdded != 1 and 's' or '')
    450              )
    451              return self.callZenScreen(REQUEST)
    452          return newGps
    453
    454
    455 -    def getDataSourceClasses(self):
    456          dsClasses = [BasicDataSource, BuiltInDS]
    457          for zp in self.dmd.ZenPackManager.packs():
    458              dsClasses += zp.getDataSourceClasses()
    459          return dsClasses
    460
    461
    462 -    def getDataSourceOptions(self):
    463          ''' Returns a list of the available datasource options as a list
    464          of (display name, dsOption)
    465          '''
    466          dsTypes = []
    467          for dsClass in self.getDataSourceClasses():
    468              dsTypes += [(t, '%s.%s' % (dsClass.__name__, t))
    469                              for t in dsClass.sourcetypes]
    470          return dsTypes
    471
    472
    473 -    def getDataSourceInstance(self, id, dsOption):
    474          ''' Given one of the dsOptions returned by getDataSourceOptions)
    475          return an instance of the that RRDDataSource subclass.
    476          '''
    477          dsClassName, dsType = dsOption.split('.')
    478          for c in self.getDataSourceClasses():
    479              if dsClassName == c.__name__:
    480                  ds = c(id)
    481                  ds.sourcetype = dsType
    482                  break
    483          else:
    484              raise ConfigurationError('Cannot find datasource class'
    485                          ' for %s' % dsOption)
    486          return ds
    487
    488
    489 -    def getThresholdClasses(self):
    490          from Products.ZenModel.MinMaxThreshold import MinMaxThreshold
    491          thresholdClasses = [MinMaxThreshold]
    492          for zp in self.dmd.ZenPackManager.packs():
    493              thresholdClasses += zp.getThresholdClasses()
    494          return map(lambda x: (x, x.__name__), thresholdClasses)
    495
    496
    497 -    def getThresholdClass(self, id, thresholdClassName):
    498          ''' Given one of the dsOptions returned by getDataSourceOptions)
    499          return an instance of the that RRDDataSource subclass.
    500          '''
    501          for c, name in self.getThresholdClasses():
    502              if thresholdClassName == c.__name__:
    503                  return c(id)
    504          raise ConfigurationError('Cannot find threshold class %s' %
    505                                   thresholdClassName)
    506
    507
    508 -    def getEventClassNames(self):
    509          """
    510          Get a list of all event class names within the permission scope.
    511          """
    512          return self.primaryAq().Events.getOrganizerNames()
    513
    514
    515 -    def getUIPath(self, separator='/'):
    516          """
    517          Given a separator and a template this method returns the UI path that we display
    518          to the user.
    519          @param RRDTemplate template
    520          @param String separator e.g. '/'
    521          @returns String e.g. '/Devices' or '/Server'
    522          """
    523          obj = self.deviceClass()
    524          if obj is None:
    525              # this template is in a Device
    526              obj = aq_parent(self)
    527              path = list(obj.getPrimaryPath())
    528              # remove the "devices" relationship
    529              path.pop(-2)
    530          else:
    531              # this template is in a DeviceClass.rrdTemplates relationship
    532              path = list(obj.getPrimaryPath())
    533          parts = path[4:-1]
    534          parts.append(obj.titleOrId())
    535          return separator + separator.join(parts)
    536
    537
    538  InitializeClass(RRDTemplate)
    539