Package Products :: Package ZenRRD :: Module RRDUtil |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenRRD.RRDUtil

      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  __doc__ = """RRDUtil
     15
     16  Wrapper routines around the rrdtool library.
     17  """
     18
     19  import logging
     20  log = logging.getLogger("zen.RRDUtil")
     21
     22 -def _checkUndefined(x):
     23      """
     24      Sanity check on the min, max values
     25
     26      @param x: RRD min or max value
     27      @type x: number
     28      @return: Either the number or 'U' (for undefined)
     29      @rtype: number or string
     30      """
     31      if x is None or x == '' or x == -1 or x == '-1':
     32          return 'U'
     33      return x
     34
     35
     36 -def convertToRRDTime(val):
     37      """
     38      Convert any value that is passed in to a string that is acceptable to use
     39      for RRDtool's start and end parameters. Raises ValueError if this is not
     40      possible.
     41
     42      See the AT-STYLE TIME SPECIFICATION and TIME REFERENCE SPECIFICATION
     43      sections of the following document.
     44
     45          http://oss.oetiker.ch/rrdtool/doc/rrdfetch.en.html
     46
     47      Note: Currently this method is only fixing floats by turning them into
     48            strings with no decimal places.
     49      """
     50      # Integers are ok. This will also strip decimal precision.
     51      try:
     52          result = int(val)
     53          return str(result)
     54      except ValueError:
     55          pass
     56
     57      return str(val)
     58
     59
     60 -class RRDUtil:
     61      """
     62      Wrapper class around rrdtool
     63      """
     64
     65 -    def __init__(self, defaultRrdCreateCommand, defaultCycleTime):
     66          """
     67          Initializer
     68
     69          The RRD creation command is only used if the RRD file doesn't
     70          exist and no rrdCommand was specified with the save() method.
     71
     72          @param defaultRrdCreateCommand: RRD creation command
     73          @type defaultRrdCreateCommand: string
     74          @param defaultCycleTime: expected time to periodically collect data
     75          @type defaultCycleTime: integer
     76          """
     77          self.defaultRrdCreateCommand = defaultRrdCreateCommand
     78          self.defaultCycleTime = defaultCycleTime
     79          self.dataPoints = 0
     80          self.cycleDataPoints = 0
     81
     82
     83 -    def endCycle(self):
     84          """
     85          Report on the number of data points collected in a cycle,
     86          and reset the counter for a new cycle.
     87
     88          @return: number of data points collected during the cycle
     89          @rtype: number
     90          """
     91          result = self.cycleDataPoints
     92          self.cycleDataPoints = 0
     93          return result
     94
     95
     96 -    def performancePath(self, path):
     97          """
     98          Given a path, return its location from $ZENHOME and the
     99          perf/ directories.
    100
    101          @param path: name for a datapoint in a path (eg device/component/datasource_datapoint)
    102          @type path: string
    103          @return: absolute path
    104          @rtype: string
    105          """
    106          from Products.ZenModel.PerformanceConf import performancePath
    107          return performancePath(path)
    108
    109
    110 -    def getStep(self, cycleTime):
    111          """
    112          Return the step value for the provided cycleTime. This is a hook for
    113          altering the default step calculation.
    114          """
    115          return int(cycleTime)
    116
    117
    118 -    def getHeartbeat(self, cycleTime):
    119          """
    120          Return the heartbeat value for the provided cycleTime. This is a hook
    121          for altering the default heartbeat calculation.
    122          """
    123          return int(cycleTime) * 3
    124
    125
    126 -    def save(self, path, value, rrdType, rrdCommand=None, cycleTime=None,
    127               min='U', max='U'):
    128          """
    129          Save the value provided in the command to the RRD file specified in path.
    130
    131          If the RRD file does not exist, use the rrdType, rrdCommand, min and
    132          max parameters to create the file.
    133
    134          @param path: name for a datapoint in a path (eg device/component/datasource_datapoint)
    135          @type path: string
    136          @param value: value to store into the RRD file
    137          @type value: number
    138          @param rrdType: RRD data type (eg ABSOLUTE, DERIVE, COUNTER)
    139          @type rrdType: string
    140          @param rrdCommand: RRD file creation command
    141          @type rrdCommand: string
    142          @param cycleTime: length of a cycle
    143          @type cycleTime: number
    144          @param min: minimum value acceptable for this metric
    145          @type min: number
    146          @param max: maximum value acceptable for this metric
    147          @type max: number
    148          @return: the parameter value converted to a number
    149          @rtype: number or None
    150          """
    151          import rrdtool, os
    152
    153          if value is None: return None
    154
    155          self.dataPoints += 1
    156          self.cycleDataPoints += 1
    157
    158          if cycleTime is None:
    159              cycleTime = self.defaultCycleTime
    160
    161          filename = self.performancePath(path) + '.rrd'
    162          if not rrdCommand:
    163              rrdCommand = self.defaultRrdCreateCommand
    164          if not os.path.exists(filename):
    165              log.debug("Creating new RRD file %s", filename)
    166              dirname = os.path.dirname(filename)
    167              if not os.path.exists(dirname):
    168                  os.makedirs(dirname, 0750)
    169
    170              min, max = map(_checkUndefined, (min, max))
    171              dataSource = 'DS:%s:%s:%d:%s:%s' % (
    172                  'ds0', rrdType, self.getHeartbeat(cycleTime), min, max)
    173              rrdtool.create(str(filename), "--step",
    174                  str(self.getStep(cycleTime)),
    175                  str(dataSource), *rrdCommand.split())
    176
    177          if rrdType in ('COUNTER', 'DERIVE'):
    178              try:
    179                  value = long(value)
    180              except (TypeError, ValueError):
    181                  return None
    182          else:
    183              value = float(value)
    184          try:
    185              rrdtool.update(str(filename), 'N:%s' % value)
    186              log.debug('%s: %r', str(filename), value)
    187          except rrdtool.error, err:
    188              # may get update errors when updating too quickly
    189              log.error('rrdtool reported error %s %s', err, path)
    190
    191          if rrdType in ('COUNTER', 'DERIVE'):
    192              startStop, names, values = \
    193                  rrdtool.fetch(filename, 'AVERAGE',
    194                      '-s', 'now-%d' % (cycleTime*2),
    195                      '-e', 'now')
    196              values = [ v[0] for v in values if v[0] is not None ]
    197              if values: value = values[-1]
    198              else: value = None
    199          return value
    200