Code

Imported upstream SVN snapshot 1.4~rc2+20090928.
[pkg-rrdtool.git] / doc / cdeftutorial.html
1 <?xml version="1.0" ?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml">
4 <head>
5 <title>cdeftutorial</title>
6 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
7 <link rev="made" href="mailto:root@localhost" />
8 </head>
10 <body style="background-color: white">
13 <!-- INDEX BEGIN -->
14 <div name="index">
15 <p><a name="__index__"></a></p>
16 <!--
18 <ul>
20         <li><a href="#name">NAME</a></li>
21         <li><a href="#description">DESCRIPTION</a></li>
22         <ul>
24                 <li><a href="#why_this_tutorial">Why this tutorial?</a></li>
25                 <li><a href="#more_reading">More reading</a></li>
26         </ul>
28         <li><a href="#what_are_cdefs">What are CDEFs?</a></li>
29         <li><a href="#syntax">Syntax</a></li>
30         <li><a href="#rpn_expressions">RPN-expressions</a></li>
31         <li><a href="#converting_your_wishes_to_rpn">Converting your wishes to RPN</a></li>
32         <li><a href="#some_special_numbers">Some special numbers</a></li>
33         <ul>
35                 <li><a href="#the_unknown_value">The unknown value</a></li>
36                 <li><a href="#working_with_unknown_data_in_your_database">Working with unknown data in your database</a></li>
37                 <li><a href="#infinity">Infinity</a></li>
38                 <li><a href="#working_with_unknown_data_and_infinity">Working with unknown data and infinity</a></li>
39         </ul>
41         <li><a href="#some_examples">Some examples</a></li>
42         <ul>
44                 <li><a href="#example__using_a_recently_created_rrd">Example: using a recently created RRD</a></li>
45                 <li><a href="#example__better_handling_of_unknown_data__by_using_time">Example: better handling of unknown data, by using time</a></li>
46                 <li><a href="#example__pretending_weird_data_isn_t_there">Example: Pretending weird data isn't there</a></li>
47                 <li><a href="#example__working_on_a_certain_time_span">Example: working on a certain time span</a></li>
48                 <li><a href="#example__you_suspect_to_have_problems_and_want_to_see_unknown_data_">Example: You suspect to have problems and want to see unknown data.</a></li>
49                 <li><a href="#same_example_useful_with_stacked_data_">Same example useful with STACKed data:</a></li>
50         </ul>
52         <li><a href="#the_examples_from_the_rrd_graph_manual_page">The examples from the RRD graph manual page</a></li>
53         <ul>
55                 <li><a href="#degrees_celsius_vs__degrees_fahrenheit">Degrees Celsius vs. Degrees Fahrenheit</a></li>
56                 <li><a href="#changing_unknown_into_zero">Changing unknown into zero</a></li>
57                 <li><a href="#infinity_demo">Infinity demo</a></li>
58                 <li><a href="#filtering_data">Filtering data</a></li>
59         </ul>
61         <li><a href="#out_of_ideas_for_now">Out of ideas for now</a></li>
62         <li><a href="#see_also">SEE ALSO</a></li>
63         <li><a href="#author">AUTHOR</a></li>
64 </ul>
66 -->
69 </div>
70 <!-- INDEX END -->
72 <p>
73 </p>
74 <h1><a name="name">NAME</a></h1>
75 <p>cdeftutorial - Alex van den Bogaerdt's CDEF tutorial</p>
76 <p>
77 </p>
78 <hr />
79 <h1><a name="description">DESCRIPTION</a></h1>
80 <p>Intention of this document: to provide some examples of the commonly
81 used parts of RRDtool's CDEF language.</p>
82 <p>If you think some important feature is not explained properly, and if
83 adding it to this document would benefit most users, please do ask me
84 to add it.  I will then try to provide an answer in the next release
85 of this tutorial.  No feedback equals no changes! Additions to
86 this document are also welcome.  -- Alex van den Bogaerdt
87 &lt;<a href="mailto:alex@vandenbogaerdt.nl">alex@vandenbogaerdt.nl</a>&gt;</p>
88 <p>
89 </p>
90 <h2><a name="why_this_tutorial">Why this tutorial?</a></h2>
91 <p>One of the powerful parts of RRDtool is its ability to do all sorts
92 of calculations on the data retrieved from its databases. However,
93 RRDtool's many options and syntax make it difficult for the average
94 user to understand. The manuals are good at explaining what these
95 options do; however they do not (and should not) explain in detail
96 why they are useful. As with my RRDtool tutorial: if you want a
97 simple document in simple language you should read this tutorial.
98 If you are happy with the official documentation, you may find this
99 document too simple or even boring. If you do choose to read this
100 tutorial, I also expect you to have read and fully understand my
101 other tutorial.</p>
102 <p>
103 </p>
104 <h2><a name="more_reading">More reading</a></h2>
105 <p>If you have difficulties with the way I try to explain it please read
106 Steve Rader's <a href="././rpntutorial.html">the rpntutorial manpage</a>. It may help you understand how this all works.</p>
107 <p>
108 </p>
109 <hr />
110 <h1><a name="what_are_cdefs">What are CDEFs?</a></h1>
111 <p>When retrieving data from an RRD, you are using a &quot;DEF&quot; to work with
112 that data. Think of it as a variable that changes over time (where
113 time is the x-axis). The value of this variable is what is found in
114 the database at that particular time and you can't do any
115 modifications on it. This is what CDEFs are for: they takes values
116 from DEFs and perform calculations on them.</p>
117 <p>
118 </p>
119 <hr />
120 <h1><a name="syntax">Syntax</a></h1>
121 <pre>
122    DEF:var_name_1=some.rrd:ds_name:CF
123    CDEF:var_name_2=RPN_expression</pre>
124 <p>You first define &quot;var_name_1&quot; to be data collected from data source
125 &quot;ds_name&quot; found in RRD &quot;some.rrd&quot; with consolidation function &quot;CF&quot;.</p>
126 <p>Assume the ifInOctets SNMP counter is saved in mrtg.rrd as the DS &quot;in&quot;.
127 Then the following DEF defines a variable for the average of that
128 data source:</p>
129 <pre>
130    DEF:inbytes=mrtg.rrd:in:AVERAGE</pre>
131 <p>Say you want to display bits per second (instead of bytes per second
132 as stored in the database.)  You have to define a calculation
133 (hence &quot;CDEF&quot;) on variable &quot;inbytes&quot; and use that variable (inbits)
134 instead of the original:</p>
135 <pre>
136    CDEF:inbits=inbytes,8,*</pre>
137 <p>This tells RRDtool to multiply inbytes by eight to get inbits. I'll
138 explain later how this works. In the graphing or printing functions,
139 you can now use inbits where you would use inbytes otherwise.</p>
140 <p>Note that the variable name used in the CDEF (inbits) must not be the
141 same as the variable named in the DEF (inbytes)!</p>
142 <p>
143 </p>
144 <hr />
145 <h1><a name="rpn_expressions">RPN-expressions</a></h1>
146 <p>RPN is short-hand for Reverse Polish Notation. It works as follows.
147 You put the variables or numbers on a stack. You also put operations
148 (things-to-do) on the stack and this stack is then processed. The result
149 will be placed on the stack. At the end, there should be exactly one
150 number left: the outcome of the series of operations. If there is not
151 exactly one number left, RRDtool will complain loudly.</p>
152 <p>Above multiplication by eight will look like:</p>
153 <ol>
154 <li>
155 <p>Start with an empty stack</p>
156 </li>
157 <li>
158 <p>Put the content of variable inbytes on the stack</p>
159 </li>
160 <li>
161 <p>Put the number eight on the stack</p>
162 </li>
163 <li>
164 <p>Put the operation multiply on the stack</p>
165 </li>
166 <li>
167 <p>Process the stack</p>
168 </li>
169 <li>
170 <p>Retrieve the value from the stack and put it in variable inbits</p>
171 </li>
172 </ol>
173 <p>We will now do an example with real numbers. Suppose the variable
174 inbytes would have value 10, the stack would be:</p>
175 <ol>
176 <li>
177 <p>||</p>
178 </li>
179 <li>
180 <p>|10|</p>
181 </li>
182 <li>
183 <p>|10|8|</p>
184 </li>
185 <li>
186 <p>|10|8|*|</p>
187 </li>
188 <li>
189 <p>|80|</p>
190 </li>
191 <li>
192 <p>||</p>
193 </li>
194 </ol>
195 <p>Processing the stack (step 5) will retrieve one value from the stack
196 (from the right at step 4). This is the operation multiply and this
197 takes two values off the stack as input. The result is put back on the
198 stack (the value 80 in this case). For multiplication the order doesn't
199 matter, but for other operations like subtraction and division it does.
200 Generally speaking you have the following order:</p>
201 <pre>
202    y = A - B  --&gt;  y=minus(A,B)  --&gt;  CDEF:y=A,B,-</pre>
203 <p>This is not very intuitive (at least most people don't think so). For
204 the function f(A,B) you reverse the position of &quot;f&quot;, but you do not
205 reverse the order of the variables.</p>
206 <p>
207 </p>
208 <hr />
209 <h1><a name="converting_your_wishes_to_rpn">Converting your wishes to RPN</a></h1>
210 <p>First, get a clear picture of what you want to do. Break down the problem
211 in smaller portions until they cannot be split anymore. Then it is rather
212 simple to convert your ideas into RPN.</p>
213 <p>Suppose you have several RRDs and would like to add up some counters in
214 them. These could be, for instance, the counters for every WAN link you
215 are monitoring.</p>
216 <p>You have:</p>
217 <pre>
218    router1.rrd with link1in link2in
219    router2.rrd with link1in link2in
220    router3.rrd with link1in link2in</pre>
221 <p>Suppose you would like to add up all these counters, except for link2in
222 inside router2.rrd. You need to do:</p>
223 <p>(in this example, &quot;router1.rrd:link1in&quot; means the DS link1in inside the
224 RRD router1.rrd)</p>
225 <pre>
226    router1.rrd:link1in
227    router1.rrd:link2in
228    router2.rrd:link1in
229    router3.rrd:link1in
230    router3.rrd:link2in
231    --------------------   +
232    (outcome of the sum)</pre>
233 <p>As a mathematical function, this could be written:</p>
234 <p><code>add(router1.rrd:link1in , router1.rrd:link2in , router2.rrd:link1in , router3.rrd:link1in , router3.rrd:link2.in)</code></p>
235 <p>With RRDtool and RPN, first, define the inputs:</p>
236 <pre>
237    DEF:a=router1.rrd:link1in:AVERAGE
238    DEF:b=router1.rrd:link2in:AVERAGE
239    DEF:c=router2.rrd:link1in:AVERAGE
240    DEF:d=router3.rrd:link1in:AVERAGE
241    DEF:e=router3.rrd:link2in:AVERAGE</pre>
242 <p>Now, the mathematical function becomes: <code>add(a,b,c,d,e)</code></p>
243 <p>In RPN, there's no operator that sums more than two values so you need
244 to do several additions. You add a and b, add c to the result, add d
245 to the result and add e to the result.</p>
246 <pre>
247    push a:         a     stack contains the value of a
248    push b and add: b,+   stack contains the result of a+b
249    push c and add: c,+   stack contains the result of a+b+c
250    push d and add: d,+   stack contains the result of a+b+c+d
251    push e and add: e,+   stack contains the result of a+b+c+d+e</pre>
252 <p>What was calculated here would be written down as:</p>
253 <pre>
254    ( ( ( (a+b) + c) + d) + e) &gt;</pre>
255 <p>This is in RPN:  <code>CDEF:result=a,b,+,c,+,d,+,e,+</code></p>
256 <p>This is correct but it can be made more clear to humans. It does
257 not matter if you add a to b and then add c to the result or first
258 add b to c and then add a to the result. This makes it possible to
259 rewrite the RPN into <code>CDEF:result=a,b,c,d,e,+,+,+,+</code> which is
260 evaluated differently:</p>
261 <pre>
262    push value of variable a on the stack: a
263    push value of variable b on the stack: a b
264    push value of variable c on the stack: a b c
265    push value of variable d on the stack: a b c d
266    push value of variable e on the stack: a b c d e
267    push operator + on the stack:          a b c d e +
268    and process it:                        a b c P   (where P == d+e)
269    push operator + on the stack:          a b c P +
270    and process it:                        a b Q     (where Q == c+P)
271    push operator + on the stack:          a b Q +
272    and process it:                        a R       (where R == b+Q)
273    push operator + on the stack:          a R +
274    and process it:                        S         (where S == a+R)</pre>
275 <p>As you can see the RPN expression <code>a,b,c,d,e,+,+,+,+,+</code> will evaluate in
276 <code>((((d+e)+c)+b)+a)</code> and it has the same outcome as <code>a,b,+,c,+,d,+,e,+</code>.
277 This is called the commutative law of addition,
278 but you may forget this right away, as long as you remember what it
279 means.</p>
280 <p>Now look at an expression that contains a multiplication:</p>
281 <p>First in normal math: <code>let result = a+b*c</code>. In this case you can't
282 choose the order yourself, you have to start with the multiplication
283 and then add a to it. You may alter the position of b and c, you must
284 not alter the position of a and b.</p>
285 <p>You have to take this in consideration when converting this expression
286 into RPN. Read it as: &quot;Add the outcome of b*c to a&quot; and then it is
287 easy to write the RPN expression: <code>result=a,b,c,*,+</code>
288 Another expression that would return the same: <code>result=b,c,*,a,+</code></p>
289 <p>In normal math, you may encounter something like &quot;a*(b+c)&quot; and this
290 can also be converted into RPN. The parenthesis just tell you to first
291 add b and c, and then multiply a with the result. Again, now it is
292 easy to write it in RPN: <code>result=a,b,c,+,*</code>. Note that this is very
293 similar to one of the expressions in the previous paragraph, only the
294 multiplication and the addition changed places.</p>
295 <p>When you have problems with RPN or when RRDtool is complaining, it's
296 usually a good thing to write down the stack on a piece of paper
297 and see what happens. Have the manual ready and pretend to be RRDtool.
298 Just do all the math by hand to see what happens, I'm sure this will
299 solve most, if not all, problems you encounter.</p>
300 <p>
301 </p>
302 <hr />
303 <h1><a name="some_special_numbers">Some special numbers</a></h1>
304 <p>
305 </p>
306 <h2><a name="the_unknown_value">The unknown value</a></h2>
307 <p>Sometimes collecting your data will fail. This can be very common,
308 especially when querying over busy links. RRDtool can be configured
309 to allow for one (or even more) unknown value(s) and calculate the missing
310 update. You can, for instance, query your device every minute. This is
311 creating one so called PDP or primary data point per minute. If you
312 defined your RRD to contain an RRA that stores 5-minute values, you need
313 five of those PDPs to create one CDP (consolidated data point).
314 These PDPs can become unknown in two cases:</p>
315 <ol>
316 <li>
317 <p>The updates are too far apart. This is tuned using the &quot;heartbeat&quot; setting.</p>
318 </li>
319 <li>
320 <p>The update was set to unknown on purpose by inserting no value (using the
321 template option) or by using &quot;U&quot; as the value to insert.</p>
322 </li>
323 </ol>
324 <p>When a CDP is calculated, another mechanism determines if this CDP is valid
325 or not. If there are too many PDPs unknown, the CDP is unknown as well.
326 This is determined by the xff factor. Please note that one unknown counter
327 update can result in two unknown PDPs! If you only allow for one unknown
328 PDP per CDP, this makes the CDP go unknown!</p>
329 <p>Suppose the counter increments with one per second and you retrieve it
330 every minute:</p>
331 <pre>
332    counter value    resulting rate
333    10'000
334    10'060            1; (10'060-10'000)/60 == 1
335    10'120            1; (10'120-10'060)/60 == 1
336    unknown           unknown; you don't know the last value
337    10'240            unknown; you don't know the previous value
338    10'300            1; (10'300-10'240)/60 == 1</pre>
339 <p>If the CDP was to be calculated from the last five updates, it would get
340 two unknown PDPs and three known PDPs. If xff would have been set to 0.5
341 which by the way is a commonly used factor, the CDP would have a known
342 value of 1. If xff would have been set to 0.2 then the resulting CDP
343 would be unknown.</p>
344 <p>You have to decide the proper values for heartbeat, number of PDPs per
345 CDP and the xff factor. As you can see from the previous text they define
346 the behavior of your RRA.</p>
347 <p>
348 </p>
349 <h2><a name="working_with_unknown_data_in_your_database">Working with unknown data in your database</a></h2>
350 <p>As you have read in the previous chapter, entries in an RRA can be
351 set to the unknown value. If you do calculations with this type of
352 value, the result has to be unknown too. This means that an expression
353 such as <code>result=a,b,+</code> will be unknown if either a or b is unknown.
354 It would be wrong to just ignore the unknown value and return the value
355 of the other parameter. By doing so, you would assume &quot;unknown&quot; means &quot;zero&quot;
356 and this is not true.</p>
357 <p>There has been a case where somebody was collecting data for over a year.
358 A new piece of equipment was installed, a new RRD was created and the
359 scripts were changed to add a counter from the old database and a counter
360 from the new database. The result was disappointing, a large part of
361 the statistics seemed to have vanished mysteriously ...
362 They of course didn't, values from the old database (known values) were
363 added to values from the new database (unknown values) and the result was
364 unknown.</p>
365 <p>In this case, it is fairly reasonable to use a CDEF that alters unknown
366 data into zero. The counters of the device were unknown (after all, it
367 wasn't installed yet!) but you know that the data rate through the device
368 had to be zero (because of the same reason: it was not installed).</p>
369 <p>There are some examples below that make this change.</p>
370 <p>
371 </p>
372 <h2><a name="infinity">Infinity</a></h2>
373 <p>Infinite data is another form of a special number. It cannot be
374 graphed because by definition you would never reach the infinite
375 value. You can think of positive and negative infinity depending on
376 the position relative to zero.</p>
377 <p>RRDtool is capable of representing (-not- graphing!) infinity by stopping
378 at its current maximum (for positive infinity) or minimum (for negative
379 infinity) without knowing this maximum (minimum).</p>
380 <p>Infinity in RRDtool is mostly used to draw an AREA without knowing its
381 vertical dimensions. You can think of it as drawing an AREA with an
382 infinite height and displaying only the part that is visible in the
383 current graph. This is probably a good way to approximate infinity
384 and it sure allows for some neat tricks. See below for examples.</p>
385 <p>
386 </p>
387 <h2><a name="working_with_unknown_data_and_infinity">Working with unknown data and infinity</a></h2>
388 <p>Sometimes you would like to discard unknown data and pretend it is zero
389 (or any other value for that matter) and sometimes you would like to
390 pretend that known data is unknown (to discard known-to-be-wrong data).
391 This is why CDEFs have support for unknown data. There are also examples
392 available that show unknown data by using infinity.</p>
393 <p>
394 </p>
395 <hr />
396 <h1><a name="some_examples">Some examples</a></h1>
397 <p>
398 </p>
399 <h2><a name="example__using_a_recently_created_rrd">Example: using a recently created RRD</a></h2>
400 <p>You are keeping statistics on your router for over a year now. Recently
401 you installed an extra router and you would like to show the combined
402 throughput for these two devices.</p>
403 <p>If you just add up the counters from router.rrd and router2.rrd, you
404 will add known data (from router.rrd) to unknown data (from router2.rrd) for
405 the bigger part of your stats. You could solve this in a few ways:</p>
406 <ul>
407 <li>
408 <p>While creating the new database, fill it with zeros from the start to now.
409 You have to make the database start at or before the least recent time in
410 the other database.</p>
411 </li>
412 <li>
413 <p>Alternatively, you could use CDEF and alter unknown data to zero.</p>
414 </li>
415 </ul>
416 <p>Both methods have their pros and cons. The first method is troublesome and
417 if you want to do that you have to figure it out yourself. It is not
418 possible to create a database filled with zeros, you have to put them in
419 manually. Implementing the second method is described next:</p>
420 <p>What we want is: &quot;if the value is unknown, replace it with zero&quot;. This
421 could be written in pseudo-code as:  if (value is unknown) then (zero)
422 else (value). When reading the <a href="././rrdgraph.html">the rrdgraph manpage</a> manual you notice the &quot;UN&quot;
423 function that returns zero or one. You also notice the &quot;IF&quot; function
424 that takes zero or one as input.</p>
425 <p>First look at the &quot;IF&quot; function. It takes three values from the stack,
426 the first value is the decision point, the second value is returned to
427 the stack if the evaluation is &quot;true&quot; and if not, the third value is
428 returned to the stack. We want the &quot;UN&quot; function to decide what happens
429 so we combine those two functions in one CDEF.</p>
430 <p>Lets write down the two possible paths for the &quot;IF&quot; function:</p>
431 <pre>
432    if true  return a
433    if false return b</pre>
434 <p>In RPN:  <code>result=x,a,b,IF</code> where &quot;x&quot; is either true or false.</p>
435 <p>Now we have to fill in &quot;x&quot;, this should be the &quot;(value is unknown)&quot; part
436 and this is in RPN:  <code>result=value,UN</code></p>
437 <p>We now combine them: <code>result=value,UN,a,b,IF</code> and when we fill in the
438 appropriate things for &quot;a&quot; and &quot;b&quot; we're finished:</p>
439 <p><code>CDEF:result=value,UN,0,value,IF</code></p>
440 <p>You may want to read Steve Rader's RPN guide if you have difficulties
441 with the way I explained this last example.</p>
442 <p>If you want to check this RPN expression, just mimic RRDtool behavior:</p>
443 <pre>
444    For any known value, the expression evaluates as follows:
445    CDEF:result=value,UN,0,value,IF  (value,UN) is not true so it becomes 0
446    CDEF:result=0,0,value,IF         &quot;IF&quot; will return the 3rd value
447    CDEF:result=value                The known value is returned</pre>
448 <pre>
449    For the unknown value, this happens:
450    CDEF:result=value,UN,0,value,IF  (value,UN) is true so it becomes 1
451    CDEF:result=1,0,value,IF         &quot;IF&quot; sees 1 and returns the 2nd value
452    CDEF:result=0                    Zero is returned</pre>
453 <p>Of course, if you would like to see another value instead of zero, you
454 can use that other value.</p>
455 <p>Eventually, when all unknown data is removed from the RRD, you may want
456 to remove this rule so that unknown data is properly displayed.</p>
457 <p>
458 </p>
459 <h2><a name="example__better_handling_of_unknown_data__by_using_time">Example: better handling of unknown data, by using time</a></h2>
460 <p>The above example has one drawback. If you do log unknown data in
461 your database after installing your new equipment, it will also be
462 translated into zero and therefore you won't see that there was a
463 problem. This is not good and what you really want to do is:</p>
464 <ul>
465 <li>
466 <p>If there is unknown data, look at the time that this sample was taken.</p>
467 </li>
468 <li>
469 <p>If the unknown value is before time xxx, make it zero.</p>
470 </li>
471 <li>
472 <p>If it is after time xxx, leave it as unknown data.</p>
473 </li>
474 </ul>
475 <p>This is doable: you can compare the time that the sample was taken
476 to some known time. Assuming you started to monitor your device on
477 Friday September 17, 1999, 00:35:57 MET DST. Translate this time in seconds
478 since 1970-01-01 and it becomes 937'521'357. If you process unknown values
479 that were received after this time, you want to leave them unknown and
480 if they were &quot;received&quot; before this time, you want to translate them
481 into zero (so you can effectively ignore them while adding them to your
482 other routers counters).</p>
483 <p>Translating Friday September 17, 1999, 00:35:57 MET DST into 937'521'357 can
484 be done by, for instance, using gnu date:</p>
485 <pre>
486    date -d &quot;19990917 00:35:57&quot; +%s</pre>
487 <p>You could also dump the database and see where the data starts to be
488 known. There are several other ways of doing this, just pick one.</p>
489 <p>Now we have to create the magic that allows us to process unknown
490 values different depending on the time that the sample was taken.
491 This is a three step process:</p>
492 <ol>
493 <li>
494 <p>If the timestamp of the value is after 937'521'357, leave it as is.</p>
495 </li>
496 <li>
497 <p>If the value is a known value, leave it as is.</p>
498 </li>
499 <li>
500 <p>Change the unknown value into zero.</p>
501 </li>
502 </ol>
503 <p>Lets look at part one:</p>
504 <pre>
505     if (true) return the original value</pre>
506 <p>We rewrite this:</p>
507 <pre>
508     if (true) return &quot;a&quot;
509     if (false) return &quot;b&quot;</pre>
510 <p>We need to calculate true or false from step 1. There is a function
511 available that returns the timestamp for the current sample. It is
512 called, how surprisingly, &quot;TIME&quot;. This time has to be compared to
513 a constant number, we need &quot;GT&quot;. The output of &quot;GT&quot; is true or false
514 and this is good input to &quot;IF&quot;. We want &quot;if (time &gt; 937521357) then
515 (return a) else (return b)&quot;.</p>
516 <p>This process was already described thoroughly in the previous chapter
517 so lets do it quick:</p>
518 <pre>
519    if (x) then a else b
520       where x represents &quot;time&gt;937521357&quot;
521       where a represents the original value
522       where b represents the outcome of the previous example</pre>
523 <pre>
524    time&gt;937521357       --&gt; TIME,937521357,GT</pre>
525 <pre>
526    if (x) then a else b --&gt; x,a,b,IF
527    substitute x         --&gt; TIME,937521357,GT,a,b,IF
528    substitute a         --&gt; TIME,937521357,GT,value,b,IF
529    substitute b         --&gt; TIME,937521357,GT,value,value,UN,0,value,IF,IF</pre>
530 <p>We end up with:
531 <code>CDEF:result=TIME,937521357,GT,value,value,UN,0,value,IF,IF</code></p>
532 <p>This looks very complex, however, as you can see, it was not too hard to
533 come up with.</p>
534 <p>
535 </p>
536 <h2><a name="example__pretending_weird_data_isn_t_there">Example: Pretending weird data isn't there</a></h2>
537 <p>Suppose you have a problem that shows up as huge spikes in your graph.
538 You know this happens and why, so you decide to work around the problem.
539 Perhaps you're using your network to do a backup at night and by doing
540 so you get almost 10mb/s while the rest of your network activity does
541 not produce numbers higher than 100kb/s.</p>
542 <p>There are two options:</p>
543 <ol>
544 <li>
545 <p>If the number exceeds 100kb/s it is wrong and you want it masked out
546 by changing it into unknown.</p>
547 </li>
548 <li>
549 <p>You don't want the graph to show more than 100kb/s.</p>
550 </li>
551 </ol>
552 <p>Pseudo code: if (number &gt; 100) then unknown else number
553 or
554 Pseudo code: if (number &gt; 100) then 100 else number.</p>
555 <p>The second &quot;problem&quot; may also be solved by using the rigid option of
556 RRDtool graph, however this has not the same result. In this example
557 you can end up with a graph that does autoscaling. Also, if you use
558 the numbers to display maxima they will be set to 100kb/s.</p>
559 <p>We use &quot;IF&quot; and &quot;GT&quot; again. &quot;if (x) then (y) else (z)&quot; is written
560 down as &quot;CDEF:result=x,y,z,IF&quot;; now fill in x, y and z.
561 For x you fill in &quot;number greater than 100kb/s&quot; becoming
562 &quot;number,100000,GT&quot; (kilo is 1'000 and b/s is what we measure!).
563 The &quot;z&quot; part is &quot;number&quot; in both cases and the &quot;y&quot; part is either
564 &quot;UNKN&quot; for unknown or &quot;100000&quot; for 100kb/s.</p>
565 <p>The two CDEF expressions would be:</p>
566 <pre>
567     CDEF:result=number,100000,GT,UNKN,number,IF
568     CDEF:result=number,100000,GT,100000,number,IF</pre>
569 <p>
570 </p>
571 <h2><a name="example__working_on_a_certain_time_span">Example: working on a certain time span</a></h2>
572 <p>If you want a graph that spans a few weeks, but would only want to
573 see some routers' data for one week, you need to &quot;hide&quot; the rest of
574 the time frame. Don't ask me when this would be useful, it's just
575 here for the example :)</p>
576 <p>We need to compare the time stamp to a begin date and an end date.
577 Comparing isn't difficult:</p>
578 <pre>
579         TIME,begintime,GE
580         TIME,endtime,LE</pre>
581 <p>These two parts of the CDEF produce either 0 for false or 1 for true.
582 We can now check if they are both 0 (or 1) using a few IF statements
583 but, as Wataru Satoh pointed out, we can use the &quot;*&quot; or &quot;+&quot; functions
584 as logical AND and logical OR.</p>
585 <p>For &quot;*&quot;, the result will be zero (false) if either one of the two
586 operators is zero.  For &quot;+&quot;, the result will only be false (0) when
587 two false (0) operators will be added.  Warning: *any* number not
588 equal to 0 will be considered &quot;true&quot;. This means that, for instance,
589 &quot;-1,1,+&quot; (which should be &quot;true or true&quot;) will become FALSE ...
590 In other words, use &quot;+&quot; only if you know for sure that you have positive
591 numbers (or zero) only.</p>
592 <p>Let's compile the complete CDEF:</p>
593 <pre>
594         DEF:ds0=router1.rrd:AVERAGE
595         CDEF:ds0modified=TIME,begintime,GT,TIME,endtime,LE,*,ds0,UNKN,IF</pre>
596 <p>This will return the value of ds0 if both comparisons return true. You
597 could also do it the other way around:</p>
598 <pre>
599         DEF:ds0=router1.rrd:AVERAGE
600         CDEF:ds0modified=TIME,begintime,LT,TIME,endtime,GT,+,UNKN,ds0,IF</pre>
601 <p>This will return an UNKNOWN if either comparison returns true.</p>
602 <p>
603 </p>
604 <h2><a name="example__you_suspect_to_have_problems_and_want_to_see_unknown_data_">Example: You suspect to have problems and want to see unknown data.</a></h2>
605 <p>Suppose you add up the number of active users on several terminal servers.
606 If one of them doesn't give an answer (or an incorrect one) you get &quot;NaN&quot;
607 in the database (&quot;Not a Number&quot;) and NaN is evaluated as Unknown.</p>
608 <p>In this case, you would like to be alerted to it and the sum of the
609 remaining values is of no value to you.</p>
610 <p>It would be something like:</p>
611 <pre>
612     DEF:users1=location1.rrd:onlineTS1:LAST
613     DEF:users2=location1.rrd:onlineTS2:LAST
614     DEF:users3=location2.rrd:onlineTS1:LAST
615     DEF:users4=location2.rrd:onlineTS2:LAST
616     CDEF:allusers=users1,users2,users3,users4,+,+,+</pre>
617 <p>If you now plot allusers, unknown data in one of users1..users4 will
618 show up as a gap in your graph. You want to modify this to show a
619 bright red line, not a gap.</p>
620 <p>Define an extra CDEF that is unknown if all is okay and is infinite if
621 there is an unknown value:</p>
622 <pre>
623     CDEF:wrongdata=allusers,UN,INF,UNKN,IF</pre>
624 <p>&quot;allusers,UN&quot; will evaluate to either true or false, it is the (x) part
625 of the &quot;IF&quot; function and it checks if allusers is unknown.
626 The (y) part of the &quot;IF&quot; function is set to &quot;INF&quot; (which means infinity)
627 and the (z) part of the function returns &quot;UNKN&quot;.</p>
628 <p>The logic is: if (allusers == unknown) then return INF else return UNKN.</p>
629 <p>You can now use AREA to display this &quot;wrongdata&quot; in bright red. If it
630 is unknown (because allusers is known) then the red AREA won't show up.
631 If the value is INF (because allusers is unknown) then the red AREA will
632 be filled in on the graph at that particular time.</p>
633 <pre>
634    AREA:allusers#0000FF:combined user count
635    AREA:wrongdata#FF0000:unknown data</pre>
636 <p>
637 </p>
638 <h2><a name="same_example_useful_with_stacked_data_">Same example useful with STACKed data:</a></h2>
639 <p>If you use stack in the previous example (as I would do) then you don't
640 add up the values. Therefore, there is no relationship between the
641 four values and you don't get a single value to test.
642 Suppose users3 would be unknown at one point in time: users1 is plotted,
643 users2 is stacked on top of users1, users3 is unknown and therefore
644 nothing happens, users4 is stacked on top of users2.
645 Add the extra CDEFs anyway and use them to overlay the &quot;normal&quot; graph:</p>
646 <pre>
647    DEF:users1=location1.rrd:onlineTS1:LAST
648    DEF:users2=location1.rrd:onlineTS2:LAST
649    DEF:users3=location2.rrd:onlineTS1:LAST
650    DEF:users4=location2.rrd:onlineTS2:LAST
651    CDEF:allusers=users1,users2,users3,users4,+,+,+
652    CDEF:wrongdata=allusers,UN,INF,UNKN,IF
653    AREA:users1#0000FF:users at ts1
654    STACK:users2#00FF00:users at ts2
655    STACK:users3#00FFFF:users at ts3
656    STACK:users4#FFFF00:users at ts4
657    AREA:wrongdata#FF0000:unknown data</pre>
658 <p>If there is unknown data in one of users1..users4, the &quot;wrongdata&quot; AREA
659 will be drawn and because it starts at the X-axis and has infinite height
660 it will effectively overwrite the STACKed parts.</p>
661 <p>You could combine the two CDEF lines into one (we don't use &quot;allusers&quot;)
662 if you like.  But there are good reasons for writing two CDEFS:</p>
663 <ul>
664 <li>
665 <p>It improves the readability of the script.</p>
666 </li>
667 <li>
668 <p>It can be used inside GPRINT to display the total number of users.</p>
669 </li>
670 </ul>
671 <p>If you choose to combine them, you can substitute the &quot;allusers&quot; in the
672 second CDEF with the part after the equal sign from the first line:</p>
673 <pre>
674    CDEF:wrongdata=users1,users2,users3,users4,+,+,+,UN,INF,UNKN,IF</pre>
675 <p>If you do so, you won't be able to use these next GPRINTs:</p>
676 <pre>
677    COMMENT:&quot;Total number of users seen&quot;
678    GPRINT:allusers:MAX:&quot;Maximum: %6.0lf&quot;
679    GPRINT:allusers:MIN:&quot;Minimum: %6.0lf&quot;
680    GPRINT:allusers:AVERAGE:&quot;Average: %6.0lf&quot;
681    GPRINT:allusers:LAST:&quot;Current: %6.0lf\n&quot;</pre>
682 <p>
683 </p>
684 <hr />
685 <h1><a name="the_examples_from_the_rrd_graph_manual_page">The examples from the RRD graph manual page</a></h1>
686 <p>
687 </p>
688 <h2><a name="degrees_celsius_vs__degrees_fahrenheit">Degrees Celsius vs. Degrees Fahrenheit</a></h2>
689 <p>To convert Celsius into Fahrenheit use the formula
690 F=9/5*C+32</p>
691 <pre>
692    rrdtool graph demo.png --title=&quot;Demo Graph&quot; \
693       DEF:cel=demo.rrd:exhaust:AVERAGE \
694       CDEF:far=9,5,/,cel,*,32,+ \
695       LINE2:cel#00a000:&quot;D. Celsius&quot; \
696       LINE2:far#ff0000:&quot;D. Fahrenheit\c&quot;</pre>
697 <p>This example gets the DS called &quot;exhaust&quot; from database &quot;demo.rrd&quot;
698 and puts the values in variable &quot;cel&quot;. The CDEF used is evaluated
699 as follows:</p>
700 <pre>
701    CDEF:far=9,5,/,cel,*,32,+
702    1. push 9, push 5
703    2. push function &quot;divide&quot; and process it
704       the stack now contains 9/5
705    3. push variable &quot;cel&quot;
706    4. push function &quot;multiply&quot; and process it
707       the stack now contains 9/5*cel
708    5. push 32
709    6. push function &quot;plus&quot; and process it
710       the stack contains now the temperature in Fahrenheit</pre>
711 <p>
712 </p>
713 <h2><a name="changing_unknown_into_zero">Changing unknown into zero</a></h2>
714 <pre>
715    rrdtool graph demo.png --title=&quot;Demo Graph&quot; \
716       DEF:idat1=interface1.rrd:ds0:AVERAGE \
717       DEF:idat2=interface2.rrd:ds0:AVERAGE \
718       DEF:odat1=interface1.rrd:ds1:AVERAGE \
719       DEF:odat2=interface2.rrd:ds1:AVERAGE \
720       CDEF:agginput=idat1,UN,0,idat1,IF,idat2,UN,0,idat2,IF,+,8,* \
721       CDEF:aggoutput=odat1,UN,0,odat1,IF,odat2,UN,0,odat2,IF,+,8,* \
722       AREA:agginput#00cc00:Input Aggregate \
723       LINE1:aggoutput#0000FF:Output Aggregate</pre>
724 <p>These two CDEFs are built from several functions. It helps to split
725 them when viewing what they do. Starting with the first CDEF we would
726 get:</p>
727 <pre>
728  idat1,UN --&gt; a
729  0        --&gt; b
730  idat1    --&gt; c
731  if (a) then (b) else (c)</pre>
732 <p>The result is therefore &quot;0&quot; if it is true that &quot;idat1&quot; equals &quot;UN&quot;.
733 If not, the original value of &quot;idat1&quot; is put back on the stack.
734 Lets call this answer &quot;d&quot;. The process is repeated for the next
735 five items on the stack, it is done the same and will return answer
736 &quot;h&quot;. The resulting stack is therefore &quot;d,h&quot;.
737 The expression has been simplified to &quot;d,h,+,8,*&quot; and it will now be
738 easy to see that we add &quot;d&quot; and &quot;h&quot;, and multiply the result with eight.</p>
739 <p>The end result is that we have added &quot;idat1&quot; and &quot;idat2&quot; and in the
740 process we effectively ignored unknown values. The result is multiplied
741 by eight, most likely to convert bytes/s to bits/s.</p>
742 <p>
743 </p>
744 <h2><a name="infinity_demo">Infinity demo</a></h2>
745 <pre>
746    rrdtool graph example.png --title=&quot;INF demo&quot; \
747       DEF:val1=some.rrd:ds0:AVERAGE \
748       DEF:val2=some.rrd:ds1:AVERAGE \
749       DEF:val3=some.rrd:ds2:AVERAGE \
750       DEF:val4=other.rrd:ds0:AVERAGE \
751       CDEF:background=val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF \
752       CDEF:wipeout=val1,val2,val3,val4,+,+,+,UN,INF,UNKN,IF \
753       AREA:background#F0F0F0 \
754       AREA:val1#0000FF:Value1 \
755       STACK:val2#00C000:Value2 \
756       STACK:val3#FFFF00:Value3 \
757       STACK:val4#FFC000:Value4 \
758       AREA:whipeout#FF0000:Unknown</pre>
759 <p>This demo demonstrates two ways to use infinity. It is a bit tricky
760 to see what happens in the &quot;background&quot; CDEF.</p>
761 <pre>
762    &quot;val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF&quot;</pre>
763 <p>This RPN takes the value of &quot;val4&quot; as input and then immediately
764 removes it from the stack using &quot;POP&quot;. The stack is now empty but
765 as a side effect we now know the time that this sample was taken.
766 This time is put on the stack by the &quot;TIME&quot; function.</p>
767 <p>&quot;TIME,7200,%&quot; takes the modulo of time and 7'200 (which is two hours).
768 The resulting value on the stack will be a number in the range from
769 0 to 7199.</p>
770 <p>For people who don't know the modulo function: it is the remainder
771 after an integer division. If you divide 16 by 3, the answer would
772 be 5 and the remainder would be 1. So, &quot;16,3,%&quot; returns 1.</p>
773 <p>We have the result of &quot;TIME,7200,%&quot; on the stack, lets call this
774 &quot;a&quot;. The start of the RPN has become &quot;a,3600,LE&quot; and this checks
775 if &quot;a&quot; is less or equal than &quot;3600&quot;. It is true half of the time.
776 We now have to process the rest of the RPN and this is only a simple
777 &quot;IF&quot; function that returns either &quot;INF&quot; or &quot;UNKN&quot; depending on the
778 time. This is returned to variable &quot;background&quot;.</p>
779 <p>The second CDEF has been discussed earlier in this document so we
780 won't do that here.</p>
781 <p>Now you can draw the different layers. Start with the background
782 that is either unknown (nothing to see) or infinite (the whole
783 positive part of the graph gets filled).</p>
784 <p>Next you draw the data on top of this background, it will overlay
785 the background. Suppose one of val1..val4 would be unknown, in that
786 case you end up with only three bars stacked on top of each other.
787 You don't want to see this because the data is only valid when all
788 four variables are valid. This is why you use the second CDEF, it
789 will overlay the data with an AREA so the data cannot be seen anymore.</p>
790 <p>If your data can also have negative values you also need to overwrite
791 the other half of your graph. This can be done in a relatively simple
792 way: what you need is the &quot;wipeout&quot; variable and place a negative
793 sign before it:  &quot;CDEF:wipeout2=wipeout,-1,*&quot;</p>
794 <p>
795 </p>
796 <h2><a name="filtering_data">Filtering data</a></h2>
797 <p>You may do some complex data filtering:</p>
798 <pre>
799   MEDIAN FILTER: filters shot noise</pre>
800 <pre>
801     DEF:var=database.rrd:traffic:AVERAGE
802     CDEF:prev1=PREV(var)
803     CDEF:prev2=PREV(prev1)
804     CDEF:prev3=PREV(prev2)
805     CDEF:median=prev1,prev2,prev3,+,+,3,/
806     LINE3:median#000077:filtered
807     LINE1:prev2#007700:'raw data'</pre>
808 <pre>
809   DERIVATE:</pre>
810 <pre>
811     DEF:var=database.rrd:traffic:AVERAGE
812     CDEF:prev1=PREV(var)
813     CDEF:time=TIME
814     CDEF:prevtime=PREV(time)
815     CDEF:derivate=var,prev1,-,time,prevtime,-,/
816     LINE3:derivate#000077:derivate
817     LINE1:var#007700:'raw data'</pre>
818 <p>
819 </p>
820 <hr />
821 <h1><a name="out_of_ideas_for_now">Out of ideas for now</a></h1>
822 <p>This document was created from questions asked by either myself or by
823 other people on the RRDtool mailing list. Please let me know if you
824 find errors in it or if you have trouble understanding it. If you
825 think there should be an addition, mail me:
826 &lt;<a href="mailto:alex@vandenbogaerdt.nl">alex@vandenbogaerdt.nl</a>&gt;</p>
827 <p>Remember: <strong>No feedback equals no changes!</strong></p>
828 <p>
829 </p>
830 <hr />
831 <h1><a name="see_also">SEE ALSO</a></h1>
832 <p>The RRDtool manpages</p>
833 <p>
834 </p>
835 <hr />
836 <h1><a name="author">AUTHOR</a></h1>
837 <p>Alex van den Bogaerdt
838 &lt;<a href="mailto:alex@vandenbogaerdt.nl">alex@vandenbogaerdt.nl</a>&gt;</p>
840 </body>
842 </html>