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 or (at your 
  8  # option) any later version as published by 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  import os 
 23  import re 
 24   
 25  from Products.ZenUtils.Utils import zenPath, rrd_daemon_running 
 26   
 27   
 28  EMPTY_RRD = zenPath('perf', 'empty.rrd') 
 29   
 30   
31 -def _checkUndefined(x):
32 """ 33 Sanity check on the min, max values 34 35 @param x: RRD min or max value 36 @type x: number 37 @return: Either the number or 'U' (for undefined) 38 @rtype: number or string 39 """ 40 if x is None or x == '' or x == -1 or x == '-1': 41 return 'U' 42 return x
43 44
45 -def convertToRRDTime(val):
46 """ 47 Convert any value that is passed in to a string that is acceptable to use 48 for RRDtool's start and end parameters. Raises ValueError if this is not 49 possible. 50 51 See the AT-STYLE TIME SPECIFICATION and TIME REFERENCE SPECIFICATION 52 sections of the following document. 53 54 http://oss.oetiker.ch/rrdtool/doc/rrdfetch.en.html 55 56 Note: Currently this method is only fixing floats by turning them into 57 strings with no decimal places. 58 """ 59 # Integers are ok. This will also strip decimal precision. 60 try: 61 result = int(val) 62 return str(result) 63 except ValueError: 64 pass 65 66 return str(val)
67 68
69 -def fixMissingRRDs(gopts):
70 """ 71 Parses a list of RRDtool gopts for DEFs. Runs all of the filenames 72 referenced by those DEFs through the fixRRDFilename method to make sure 73 that they will exist and not cause the rendering to fail. 74 """ 75 fixed_gopts = [] 76 77 def_match = re.compile(r'^DEF:([^=]+)=([^:]+)').match 78 for gopt in gopts: 79 match = def_match(gopt) 80 if not match: 81 fixed_gopts.append(gopt) 82 continue 83 84 rrd_filename = match.group(2) 85 fixed_gopts.append(gopt.replace( 86 rrd_filename, fixRRDFilename(rrd_filename))) 87 88 return fixed_gopts
89 90
91 -def fixRRDFilename(filename):
92 """ 93 Attempting to render a graph containing a DEF referencing a non-existent 94 filename will cause the entire graph to fail to render. This method is a 95 helper to verify existence of an RRD file. If the file doesn't exist, a 96 placeholder RRD filename with no values in it will be returned instead. 97 """ 98 if os.path.isfile(filename): 99 return filename 100 101 if not os.path.isfile(EMPTY_RRD): 102 import rrdtool 103 rrdtool.create(EMPTY_RRD, "--step", '300', 'DS:ds0:GAUGE:900:U:U', 104 'RRA:AVERAGE:0.5:1:1', 'RRA:MAX:0.5:1:1') 105 106 return EMPTY_RRD
107
108 -def read(path, consolidationFunction, start, end):
109 import rrdtool 110 try: 111 return rrdtool.fetch(path, consolidationFunction, start, end) 112 except rrdtool.error, err: 113 import sys 114 err_str = '%s: %s' % (err.__class__.__name__, err) 115 msg = 'Failed to read RRD file %s. %s' % (path, err_str) 116 raise StandardError(msg), None, sys.exc_info()[2]
117
118 -class RRDUtil:
119 """ 120 Wrapper class around rrdtool 121 """ 122
123 - def __init__(self, defaultRrdCreateCommand, defaultCycleTime):
124 """ 125 Initializer 126 127 The RRD creation command is only used if the RRD file doesn't 128 exist and no rrdCommand was specified with the save() method. 129 130 @param defaultRrdCreateCommand: RRD creation command 131 @type defaultRrdCreateCommand: string 132 @param defaultCycleTime: expected time to periodically collect data 133 @type defaultCycleTime: integer 134 """ 135 self.defaultRrdCreateCommand = defaultRrdCreateCommand 136 self.defaultCycleTime = defaultCycleTime 137 self.dataPoints = 0 138 self.cycleDataPoints = 0
139 140
141 - def endCycle(self):
142 """ 143 Report on the number of data points collected in a cycle, 144 and reset the counter for a new cycle. 145 146 @return: number of data points collected during the cycle 147 @rtype: number 148 """ 149 result = self.cycleDataPoints 150 self.cycleDataPoints = 0 151 return result
152 153
154 - def performancePath(self, path):
155 """ 156 Given a path, return its location from $ZENHOME and the 157 perf/ directories. 158 159 @param path: name for a datapoint in a path (eg device/component/datasource_datapoint) 160 @type path: string 161 @return: absolute path 162 @rtype: string 163 """ 164 from Products.ZenModel.PerformanceConf import performancePath 165 return performancePath(path)
166 167
168 - def getStep(self, cycleTime):
169 """ 170 Return the step value for the provided cycleTime. This is a hook for 171 altering the default step calculation. 172 """ 173 return int(cycleTime)
174 175
176 - def getHeartbeat(self, cycleTime):
177 """ 178 Return the heartbeat value for the provided cycleTime. This is a hook 179 for altering the default heartbeat calculation. 180 """ 181 return int(cycleTime) * 3
182 183
184 - def save(self, path, value, rrdType, rrdCommand=None, cycleTime=None, 185 min='U', max='U', useRRDDaemon=True):
186 """ 187 Save the value provided in the command to the RRD file specified in path. 188 189 If the RRD file does not exist, use the rrdType, rrdCommand, min and 190 max parameters to create the file. 191 192 @param path: name for a datapoint in a path (eg device/component/datasource_datapoint) 193 @type path: string 194 @param value: value to store into the RRD file 195 @type value: number 196 @param rrdType: RRD data type (eg ABSOLUTE, DERIVE, COUNTER) 197 @type rrdType: string 198 @param rrdCommand: RRD file creation command 199 @type rrdCommand: string 200 @param cycleTime: length of a cycle 201 @type cycleTime: number 202 @param min: minimum value acceptable for this metric 203 @type min: number 204 @param max: maximum value acceptable for this metric 205 @type max: number 206 @return: the parameter value converted to a number 207 @rtype: number or None 208 """ 209 import rrdtool, os 210 211 daemon_args = () 212 if useRRDDaemon: 213 daemon = rrd_daemon_running() 214 if daemon: 215 daemon_args = ('--daemon', daemon) 216 217 if value is None: return None 218 219 self.dataPoints += 1 220 self.cycleDataPoints += 1 221 222 if cycleTime is None: 223 cycleTime = self.defaultCycleTime 224 225 filename = self.performancePath(path) + '.rrd' 226 if not rrdCommand: 227 rrdCommand = self.defaultRrdCreateCommand 228 if not os.path.exists(filename): 229 log.debug("Creating new RRD file %s", filename) 230 dirname = os.path.dirname(filename) 231 if not os.path.exists(dirname): 232 os.makedirs(dirname, 0750) 233 234 min, max = map(_checkUndefined, (min, max)) 235 dataSource = 'DS:%s:%s:%d:%s:%s' % ( 236 'ds0', rrdType, self.getHeartbeat(cycleTime), min, max) 237 rrdtool.create(str(filename), "--step", 238 str(self.getStep(cycleTime)), 239 str(dataSource), *rrdCommand.split()) 240 241 if rrdType in ('COUNTER', 'DERIVE'): 242 try: 243 value = long(value) 244 except (TypeError, ValueError): 245 return None 246 else: 247 try: 248 value = float(value) 249 except (TypeError, ValueError): 250 return None 251 try: 252 rrdtool.update(str(filename), *(daemon_args + ('N:%s' % value,))) 253 log.debug('%s: %r', str(filename), value) 254 except rrdtool.error, err: 255 # may get update errors when updating too quickly 256 log.error('rrdtool reported error %s %s', err, path) 257 258 if rrdType in ('COUNTER', 'DERIVE'): 259 startStop, names, values = \ 260 rrdtool.fetch(filename, 'AVERAGE', 261 '-s', 'now-%d' % (cycleTime*2), 262 '-e', 'now', *daemon_args) 263 values = [ v[0] for v in values if v[0] is not None ] 264 if values: value = values[-1] 265 else: value = None 266 return value
267