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  import os 
 23  import re 
 24   
 25  from Products.ZenUtils.Utils import zenPath 
 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
109 -class RRDUtil:
110 """ 111 Wrapper class around rrdtool 112 """ 113
114 - def __init__(self, defaultRrdCreateCommand, defaultCycleTime):
115 """ 116 Initializer 117 118 The RRD creation command is only used if the RRD file doesn't 119 exist and no rrdCommand was specified with the save() method. 120 121 @param defaultRrdCreateCommand: RRD creation command 122 @type defaultRrdCreateCommand: string 123 @param defaultCycleTime: expected time to periodically collect data 124 @type defaultCycleTime: integer 125 """ 126 self.defaultRrdCreateCommand = defaultRrdCreateCommand 127 self.defaultCycleTime = defaultCycleTime 128 self.dataPoints = 0 129 self.cycleDataPoints = 0
130 131
132 - def endCycle(self):
133 """ 134 Report on the number of data points collected in a cycle, 135 and reset the counter for a new cycle. 136 137 @return: number of data points collected during the cycle 138 @rtype: number 139 """ 140 result = self.cycleDataPoints 141 self.cycleDataPoints = 0 142 return result
143 144
145 - def performancePath(self, path):
146 """ 147 Given a path, return its location from $ZENHOME and the 148 perf/ directories. 149 150 @param path: name for a datapoint in a path (eg device/component/datasource_datapoint) 151 @type path: string 152 @return: absolute path 153 @rtype: string 154 """ 155 from Products.ZenModel.PerformanceConf import performancePath 156 return performancePath(path)
157 158
159 - def getStep(self, cycleTime):
160 """ 161 Return the step value for the provided cycleTime. This is a hook for 162 altering the default step calculation. 163 """ 164 return int(cycleTime)
165 166
167 - def getHeartbeat(self, cycleTime):
168 """ 169 Return the heartbeat value for the provided cycleTime. This is a hook 170 for altering the default heartbeat calculation. 171 """ 172 return int(cycleTime) * 3
173 174
175 - def save(self, path, value, rrdType, rrdCommand=None, cycleTime=None, 176 min='U', max='U'):
177 """ 178 Save the value provided in the command to the RRD file specified in path. 179 180 If the RRD file does not exist, use the rrdType, rrdCommand, min and 181 max parameters to create the file. 182 183 @param path: name for a datapoint in a path (eg device/component/datasource_datapoint) 184 @type path: string 185 @param value: value to store into the RRD file 186 @type value: number 187 @param rrdType: RRD data type (eg ABSOLUTE, DERIVE, COUNTER) 188 @type rrdType: string 189 @param rrdCommand: RRD file creation command 190 @type rrdCommand: string 191 @param cycleTime: length of a cycle 192 @type cycleTime: number 193 @param min: minimum value acceptable for this metric 194 @type min: number 195 @param max: maximum value acceptable for this metric 196 @type max: number 197 @return: the parameter value converted to a number 198 @rtype: number or None 199 """ 200 import rrdtool, os 201 202 if value is None: return None 203 204 self.dataPoints += 1 205 self.cycleDataPoints += 1 206 207 if cycleTime is None: 208 cycleTime = self.defaultCycleTime 209 210 filename = self.performancePath(path) + '.rrd' 211 if not rrdCommand: 212 rrdCommand = self.defaultRrdCreateCommand 213 if not os.path.exists(filename): 214 log.debug("Creating new RRD file %s", filename) 215 dirname = os.path.dirname(filename) 216 if not os.path.exists(dirname): 217 os.makedirs(dirname, 0750) 218 219 min, max = map(_checkUndefined, (min, max)) 220 dataSource = 'DS:%s:%s:%d:%s:%s' % ( 221 'ds0', rrdType, self.getHeartbeat(cycleTime), min, max) 222 rrdtool.create(str(filename), "--step", 223 str(self.getStep(cycleTime)), 224 str(dataSource), *rrdCommand.split()) 225 226 if rrdType in ('COUNTER', 'DERIVE'): 227 try: 228 value = long(value) 229 except (TypeError, ValueError): 230 return None 231 else: 232 try: 233 value = float(value) 234 except (TypeError, ValueError): 235 return None 236 try: 237 rrdtool.update(str(filename), 'N:%s' % value) 238 log.debug('%s: %r', str(filename), value) 239 except rrdtool.error, err: 240 # may get update errors when updating too quickly 241 log.error('rrdtool reported error %s %s', err, path) 242 243 if rrdType in ('COUNTER', 'DERIVE'): 244 startStop, names, values = \ 245 rrdtool.fetch(filename, 'AVERAGE', 246 '-s', 'now-%d' % (cycleTime*2), 247 '-e', 'now') 248 values = [ v[0] for v in values if v[0] is not None ] 249 if values: value = values[-1] 250 else: value = None 251 return value
252