1
2
3
4
5
6
7
8
9
10
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
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
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
51 try:
52 result = int(val)
53 return str(result)
54 except ValueError:
55 pass
56
57 return str(val)
58
59
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
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
108
109
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
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
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