1CDEFTUTORIAL(1)                     rrdtool                    CDEFTUTORIAL(1)
2
3
4

NAME

6       cdeftutorial - Alex van den Bogaerdt's CDEF tutorial
7

DESCRIPTION

9       Intention of this document: to provide some examples of the commonly
10       used parts of RRDtool's CDEF language.
11
12       If you think some important feature is not explained properly, and if
13       adding it to this document would benefit most users, please do ask me
14       to add it.  I will then try to provide an answer in the next release of
15       this tutorial.  No feedback equals no changes! Additions to this docu‐
16       ment are also welcome.  -- Alex van den Bogaerdt
17       <alex@ergens.op.het.net>
18
19       Why this tutorial?
20
21       One of the powerful parts of RRDtool is its ability to do all sorts of
22       calculations on the data retrieved from its databases. However, RRD‐
23       tool's many options and syntax make it difficult for the average user
24       to understand. The manuals are good at explaining what these options
25       do; however they do not (and should not) explain in detail why they are
26       useful. As with my RRDtool tutorial: if you want a simple document in
27       simple language you should read this tutorial.  If you are happy with
28       the official documentation, you may find this document too simple or
29       even boring. If you do choose to read this tutorial, I also expect you
30       to have read and fully understand my other tutorial.
31
32       More reading
33
34       If you have difficulties with the way I try to explain it please read
35       Steve Rader's rpntutorial. It may help you understand how this all
36       works.
37

What are CDEFs?

39       When retrieving data from an RRD, you are using a "DEF" to work with
40       that data. Think of it as a variable that changes over time (where time
41       is the x-axis). The value of this variable is what is found in the
42       database at that particular time and you can't do any modifications on
43       it. This is what CDEFs are for: they takes values from DEFs and perform
44       calculations on them.
45

Syntax

47          DEF:var_name_1=some.rrd:ds_name:CF
48          CDEF:var_name_2=RPN_expression
49
50       You first define "var_name_1" to be data collected from data source
51       "ds_name" found in RRD "some.rrd" with consolidation function "CF".
52
53       Assume the ifInOctets SNMP counter is saved in mrtg.rrd as the DS "in".
54       Then the following DEF defines a variable for the average of that data
55       source:
56
57          DEF:inbytes=mrtg.rrd:in:AVERAGE
58
59       Say you want to display bits per second (instead of bytes per second as
60       stored in the database.)  You have to define a calculation (hence
61       "CDEF") on variable "inbytes" and use that variable (inbits) instead of
62       the original:
63
64          CDEF:inbits=inbytes,8,*
65
66       This tells RRDtool to multiply inbytes by eight to get inbits. I'll
67       explain later how this works. In the graphing or printing functions,
68       you can now use inbits where you would use inbytes otherwise.
69
70       Note that the variable name used in the CDEF (inbits) must not be the
71       same as the variable named in the DEF (inbytes)!
72

RPN-expressions

74       RPN is short-hand for Reverse Polish Notation. It works as follows.
75       You put the variables or numbers on a stack. You also put operations
76       (things-to-do) on the stack and this stack is then processed. The
77       result will be placed on the stack. At the end, there should be exactly
78       one number left: the outcome of the series of operations. If there is
79       not exactly one number left, RRDtool will complain loudly.
80
81       Above multiplication by eight will look like:
82
83       1.  Start with an empty stack
84
85       2.  Put the content of variable inbytes on the stack
86
87       3.  Put the number eight on the stack
88
89       4.  Put the operation multiply on the stack
90
91       5.  Process the stack
92
93       6.  Retrieve the value from the stack and put it in variable inbits
94
95       We will now do an example with real numbers. Suppose the variable
96       inbytes would have value 10, the stack would be:
97
98       1.  ||
99
100       2.  |10|
101
102       3.  |10|8|
103
104       4.  |10|8|*|
105
106       5.  |80|
107
108       6.  ||
109
110       Processing the stack (step 5) will retrieve one value from the stack
111       (from the right at step 4). This is the operation multiply and this
112       takes two values off the stack as input. The result is put back on the
113       stack (the value 80 in this case). For multiplication the order doesn't
114       matter, but for other operations like subtraction and division it does.
115       Generally speaking you have the following order:
116
117          y = A - B  -->  y=minus(A,B)  -->  CDEF:y=A,B,-
118
119       This is not very intuitive (at least most people don't think so). For
120       the function f(A,B) you reverse the position of "f", but you do not
121       reverse the order of the variables.
122

Converting your wishes to RPN

124       First, get a clear picture of what you want to do. Break down the prob‐
125       lem in smaller portions until they cannot be split anymore. Then it is
126       rather simple to convert your ideas into RPN.
127
128       Suppose you have several RRDs and would like to add up some counters in
129       them. These could be, for instance, the counters for every WAN link you
130       are monitoring.
131
132       You have:
133
134          router1.rrd with link1in link2in
135          router2.rrd with link1in link2in
136          router3.rrd with link1in link2in
137
138       Suppose you would like to add up all these counters, except for link2in
139       inside router2.rrd. You need to do:
140
141       (in this example, "router1.rrd:link1in" means the DS link1in inside the
142       RRD router1.rrd)
143
144          router1.rrd:link1in
145          router1.rrd:link2in
146          router2.rrd:link1in
147          router3.rrd:link1in
148          router3.rrd:link2in
149          --------------------   +
150          (outcome of the sum)
151
152       As a mathematical function, this could be written:
153
154       "add(router1.rrd:link1in , router1.rrd:link2in , router2.rrd:link1in ,
155       router3.rrd:link1in , router3.rrd:link2.in)"
156
157       With RRDtool and RPN, first, define the inputs:
158
159          DEF:a=router1.rrd:link1in:AVERAGE
160          DEF:b=router1.rrd:link2in:AVERAGE
161          DEF:c=router2.rrd:link1in:AVERAGE
162          DEF:d=router3.rrd:link1in:AVERAGE
163          DEF:e=router3.rrd:link2in:AVERAGE
164
165       Now, the mathematical function becomes: "add(a,b,c,d,e)"
166
167       In RPN, there's no operator that sums more than two values so you need
168       to do several additions. You add a and b, add c to the result, add d to
169       the result and add e to the result.
170
171          push a:         a     stack contains the value of a
172          push b and add: b,+   stack contains the result of a+b
173          push c and add: c,+   stack contains the result of a+b+c
174          push d and add: d,+   stack contains the result of a+b+c+d
175          push e and add: e,+   stack contains the result of a+b+c+d+e
176
177       What was calculated here would be written down as:
178
179          ( ( ( (a+b) + c) + d) + e) >
180
181       This is in RPN:  "CDEF:result=a,b,+,c,+,d,+,e,+"
182
183       This is correct but it can be made more clear to humans. It does not
184       matter if you add a to b and then add c to the result or first add b to
185       c and then add a to the result. This makes it possible to rewrite the
186       RPN into "CDEF:result=a,b,c,d,e,+,+,+,+" which is evaluated differ‐
187       ently:
188
189          push value of variable a on the stack: a
190          push value of variable b on the stack: a b
191          push value of variable c on the stack: a b c
192          push value of variable d on the stack: a b c d
193          push value of variable e on the stack: a b c d e
194          push operator + on the stack:          a b c d e +
195          and process it:                        a b c P   (where P == d+e)
196          push operator + on the stack:          a b c P +
197          and process it:                        a b Q     (where Q == c+P)
198          push operator + on the stack:          a b Q +
199          and process it:                        a R       (where R == b+Q)
200          push operator + on the stack:          a R +
201          and process it:                        S         (where S == a+R)
202
203       As you can see the RPN expression "a,b,c,d,e,+,+,+,+,+" will evaluate
204       in "((((d+e)+c)+b)+a)" and it has the same outcome as
205       "a,b,+,c,+,d,+,e,+".  This is called the commutative law of addition,
206       but you may forget this right away, as long as you remember what it
207       means.
208
209       Now look at an expression that contains a multiplication:
210
211       First in normal math: "let result = a+b*c". In this case you can't
212       choose the order yourself, you have to start with the multiplication
213       and then add a to it. You may alter the position of b and c, you must
214       not alter the position of a and b.
215
216       You have to take this in consideration when converting this expression
217       into RPN. Read it as: "Add the outcome of b*c to a" and then it is easy
218       to write the RPN expression: "result=a,b,c,*,+" Another expression that
219       would return the same: "result=b,c,*,a,+"
220
221       In normal math, you may encounter something like "a*(b+c)" and this can
222       also be converted into RPN. The parenthesis just tell you to first add
223       b and c, and then multiply a with the result. Again, now it is easy to
224       write it in RPN: "result=a,b,c,+,*". Note that this is very similar to
225       one of the expressions in the previous paragraph, only the multiplica‐
226       tion and the addition changed places.
227
228       When you have problems with RPN or when RRDtool is complaining, it's
229       usually a good thing to write down the stack on a piece of paper and
230       see what happens. Have the manual ready and pretend to be RRDtool.
231       Just do all the math by hand to see what happens, I'm sure this will
232       solve most, if not all, problems you encounter.
233

Some special numbers

235       The unknown value
236
237       Sometimes collecting your data will fail. This can be very common,
238       especially when querying over busy links. RRDtool can be configured to
239       allow for one (or even more) unknown value(s) and calculate the missing
240       update. You can, for instance, query your device every minute. This is
241       creating one so called PDP or primary data point per minute. If you
242       defined your RRD to contain an RRA that stores 5-minute values, you
243       need five of those PDPs to create one CDP (consolidated data point).
244       These PDPs can become unknown in two cases:
245
246       1.  The updates are too far apart. This is tuned using the "heartbeat"
247           setting.
248
249       2.  The update was set to unknown on purpose by inserting no value
250           (using the template option) or by using "U" as the value to insert.
251
252       When a CDP is calculated, another mechanism determines if this CDP is
253       valid or not. If there are too many PDPs unknown, the CDP is unknown as
254       well.  This is determined by the xff factor. Please note that one
255       unknown counter update can result in two unknown PDPs! If you only
256       allow for one unknown PDP per CDP, this makes the CDP go unknown!
257
258       Suppose the counter increments with one per second and you retrieve it
259       every minute:
260
261          counter value    resulting rate
262          10'000
263          10'060            1; (10'060-10'000)/60 == 1
264          10'120            1; (10'120-10'060)/60 == 1
265          unknown           unknown; you don't know the last value
266          10'240            unknown; you don't know the previous value
267          10'300            1; (10'300-10'240)/60 == 1
268
269       If the CDP was to be calculated from the last five updates, it would
270       get two unknown PDPs and three known PDPs. If xff would have been set
271       to 0.5 which by the way is a commonly used factor, the CDP would have a
272       known value of 1. If xff would have been set to 0.2 then the resulting
273       CDP would be unknown.
274
275       You have to decide the proper values for heartbeat, number of PDPs per
276       CDP and the xff factor. As you can see from the previous text they
277       define the behavior of your RRA.
278
279       Working with unknown data in your database
280
281       As you have read in the previous chapter, entries in an RRA can be set
282       to the unknown value. If you do calculations with this type of value,
283       the result has to be unknown too. This means that an expression such as
284       "result=a,b,+" will be unknown if either a or b is unknown.  It would
285       be wrong to just ignore the unknown value and return the value of the
286       other parameter. By doing so, you would assume "unknown" means "zero"
287       and this is not true.
288
289       There has been a case where somebody was collecting data for over a
290       year.  A new piece of equipment was installed, a new RRD was created
291       and the scripts were changed to add a counter from the old database and
292       a counter from the new database. The result was disappointing, a large
293       part of the statistics seemed to have vanished mysteriously ...  They
294       of course didn't, values from the old database (known values) were
295       added to values from the new database (unknown values) and the result
296       was unknown.
297
298       In this case, it is fairly reasonable to use a CDEF that alters unknown
299       data into zero. The counters of the device were unknown (after all, it
300       wasn't installed yet!) but you know that the data rate through the
301       device had to be zero (because of the same reason: it was not
302       installed).
303
304       There are some examples below that make this change.
305
306       Infinity
307
308       Infinite data is another form of a special number. It cannot be graphed
309       because by definition you would never reach the infinite value. You can
310       think of positive and negative infinity depending on the position rela‐
311       tive to zero.
312
313       RRDtool is capable of representing (-not- graphing!) infinity by stop‐
314       ping at its current maximum (for positive infinity) or minimum (for
315       negative infinity) without knowing this maximum (minimum).
316
317       Infinity in RRDtool is mostly used to draw an AREA without knowing its
318       vertical dimensions. You can think of it as drawing an AREA with an
319       infinite height and displaying only the part that is visible in the
320       current graph. This is probably a good way to approximate infinity and
321       it sure allows for some neat tricks. See below for examples.
322
323       Working with unknown data and infinity
324
325       Sometimes you would like to discard unknown data and pretend it is zero
326       (or any other value for that matter) and sometimes you would like to
327       pretend that known data is unknown (to discard known-to-be-wrong data).
328       This is why CDEFs have support for unknown data. There are also exam‐
329       ples available that show unknown data by using infinity.
330

Some examples

332       Example: using a recently created RRD
333
334       You are keeping statistics on your router for over a year now. Recently
335       you installed an extra router and you would like to show the combined
336       throughput for these two devices.
337
338       If you just add up the counters from router.rrd and router2.rrd, you
339       will add known data (from router.rrd) to unknown data (from
340       router2.rrd) for the bigger part of your stats. You could solve this in
341       a few ways:
342
343       ·   While creating the new database, fill it with zeros from the start
344           to now.  You have to make the database start at or before the least
345           recent time in the other database.
346
347       ·   Alternatively, you could use CDEF and alter unknown data to zero.
348
349       Both methods have their pros and cons. The first method is troublesome
350       and if you want to do that you have to figure it out yourself. It is
351       not possible to create a database filled with zeros, you have to put
352       them in manually. Implementing the second method is described next:
353
354       What we want is: "if the value is unknown, replace it with zero". This
355       could be written in pseudo-code as:  if (value is unknown) then (zero)
356       else (value). When reading the rrdgraph manual you notice the "UN"
357       function that returns zero or one. You also notice the "IF" function
358       that takes zero or one as input.
359
360       First look at the "IF" function. It takes three values from the stack,
361       the first value is the decision point, the second value is returned to
362       the stack if the evaluation is "true" and if not, the third value is
363       returned to the stack. We want the "UN" function to decide what happens
364       so we combine those two functions in one CDEF.
365
366       Lets write down the two possible paths for the "IF" function:
367
368          if true  return a
369          if false return b
370
371       In RPN:  "result=x,a,b,IF" where "x" is either true or false.
372
373       Now we have to fill in "x", this should be the "(value is unknown)"
374       part and this is in RPN:  "result=value,UN"
375
376       We now combine them: "result=value,UN,a,b,IF" and when we fill in the
377       appropriate things for "a" and "b" we're finished:
378
379       "CDEF:result=value,UN,0,value,IF"
380
381       You may want to read Steve Rader's RPN guide if you have difficulties
382       with the way I explained this last example.
383
384       If you want to check this RPN expression, just mimic RRDtool behavior:
385
386          For any known value, the expression evaluates as follows:
387          CDEF:result=value,UN,0,value,IF  (value,UN) is not true so it becomes 0
388          CDEF:result=0,0,value,IF         "IF" will return the 3rd value
389          CDEF:result=value                The known value is returned
390
391          For the unknown value, this happens:
392          CDEF:result=value,UN,0,value,IF  (value,UN) is true so it becomes 1
393          CDEF:result=1,0,value,IF         "IF" sees 1 and returns the 2nd value
394          CDEF:result=0                    Zero is returned
395
396       Of course, if you would like to see another value instead of zero, you
397       can use that other value.
398
399       Eventually, when all unknown data is removed from the RRD, you may want
400       to remove this rule so that unknown data is properly displayed.
401
402       Example: better handling of unknown data, by using time
403
404       The above example has one drawback. If you do log unknown data in your
405       database after installing your new equipment, it will also be trans‐
406       lated into zero and therefore you won't see that there was a problem.
407       This is not good and what you really want to do is:
408
409       ·   If there is unknown data, look at the time that this sample was
410           taken.
411
412       ·   If the unknown value is before time xxx, make it zero.
413
414       ·   If it is after time xxx, leave it as unknown data.
415
416       This is doable: you can compare the time that the sample was taken to
417       some known time. Assuming you started to monitor your device on Friday
418       September 17, 1999, 00:35:57 MET DST. Translate this time in seconds
419       since 1970-01-01 and it becomes 937'521'357. If you process unknown
420       values that were received after this time, you want to leave them
421       unknown and if they were "received" before this time, you want to
422       translate them into zero (so you can effectively ignore them while
423       adding them to your other routers counters).
424
425       Translating Friday September 17, 1999, 00:35:57 MET DST into
426       937'521'357 can be done by, for instance, using gnu date:
427
428          date -d "19990917 00:35:57" +%s
429
430       You could also dump the database and see where the data starts to be
431       known. There are several other ways of doing this, just pick one.
432
433       Now we have to create the magic that allows us to process unknown val‐
434       ues different depending on the time that the sample was taken.  This is
435       a three step process:
436
437       1.  If the timestamp of the value is after 937'521'357, leave it as is.
438
439       2.  If the value is a known value, leave it as is.
440
441       3.  Change the unknown value into zero.
442
443       Lets look at part one:
444
445           if (true) return the original value
446
447       We rewrite this:
448
449           if (true) return "a"
450           if (false) return "b"
451
452       We need to calculate true or false from step 1. There is a function
453       available that returns the timestamp for the current sample. It is
454       called, how surprisingly, "TIME". This time has to be compared to a
455       constant number, we need "GT". The output of "GT" is true or false and
456       this is good input to "IF". We want "if (time > 937521357) then (return
457       a) else (return b)".
458
459       This process was already described thoroughly in the previous chapter
460       so lets do it quick:
461
462          if (x) then a else b
463             where x represents "time>937521357"
464             where a represents the original value
465             where b represents the outcome of the previous example
466
467          time>937521357       --> TIME,937521357,GT
468
469          if (x) then a else b --> x,a,b,IF
470          substitute x         --> TIME,937521357,GT,a,b,IF
471          substitute a         --> TIME,937521357,GT,value,b,IF
472          substitute b         --> TIME,937521357,GT,value,value,UN,0,value,IF,IF
473
474       We end up with:
475       "CDEF:result=TIME,937521357,GT,value,value,UN,0,value,IF,IF"
476
477       This looks very complex, however, as you can see, it was not too hard
478       to come up with.
479
480       Example: Pretending weird data isn't there
481
482       Suppose you have a problem that shows up as huge spikes in your graph.
483       You know this happens and why, so you decide to work around the prob‐
484       lem.  Perhaps you're using your network to do a backup at night and by
485       doing so you get almost 10mb/s while the rest of your network activity
486       does not produce numbers higher than 100kb/s.
487
488       There are two options:
489
490       1.  If the number exceeds 100kb/s it is wrong and you want it masked
491           out by changing it into unknown.
492
493       2.  You don't want the graph to show more than 100kb/s.
494
495       Pseudo code: if (number > 100) then unknown else number or Pseudo code:
496       if (number > 100) then 100 else number.
497
498       The second "problem" may also be solved by using the rigid option of
499       RRDtool graph, however this has not the same result. In this example
500       you can end up with a graph that does autoscaling. Also, if you use the
501       numbers to display maxima they will be set to 100kb/s.
502
503       We use "IF" and "GT" again. "if (x) then (y) else (z)" is written down
504       as "CDEF:result=x,y,z,IF"; now fill in x, y and z.  For x you fill in
505       "number greater than 100kb/s" becoming "number,100000,GT" (kilo is
506       1'000 and b/s is what we measure!).  The "z" part is "number" in both
507       cases and the "y" part is either "UNKN" for unknown or "100000" for
508       100kb/s.
509
510       The two CDEF expressions would be:
511
512           CDEF:result=number,100000,GT,UNKN,number,IF
513           CDEF:result=number,100000,GT,100000,number,IF
514
515       Example: working on a certain time span
516
517       If you want a graph that spans a few weeks, but would only want to see
518       some routers' data for one week, you need to "hide" the rest of the
519       time frame. Don't ask me when this would be useful, it's just here for
520       the example :)
521
522       We need to compare the time stamp to a begin date and an end date.
523       Comparing isn't difficult:
524
525               TIME,begintime,GE
526               TIME,endtime,LE
527
528       These two parts of the CDEF produce either 0 for false or 1 for true.
529       We can now check if they are both 0 (or 1) using a few IF statements
530       but, as Wataru Satoh pointed out, we can use the "*" or "+" functions
531       as logical AND and logical OR.
532
533       For "*", the result will be zero (false) if either one of the two oper‐
534       ators is zero.  For "+", the result will only be false (0) when two
535       false (0) operators will be added.  Warning: *any* number not equal to
536       0 will be considered "true". This means that, for instance, "-1,1,+"
537       (which should be "true or true") will become FALSE ...  In other words,
538       use "+" only if you know for sure that you have positive numbers (or
539       zero) only.
540
541       Let's compile the complete CDEF:
542
543               DEF:ds0=router1.rrd:AVERAGE
544               CDEF:ds0modified=TIME,begintime,GE,TIME,endtime,LE,*,UNKN,ds0,IF
545
546       This will return the value of ds0 if both comparisons return true. You
547       could also do it the other way around:
548
549               DEF:ds0=router1.rrd:AVERAGE
550               CDEF:ds0modified=TIME,begintime,LT,TIME,endtime,GT,+,UNKN,ds0,IF
551
552       This will return an UNKNOWN if either comparison returns true.
553
554       Example: You suspect to have problems and want to see unknown data.
555
556       Suppose you add up the number of active users on several terminal
557       servers.  If one of them doesn't give an answer (or an incorrect one)
558       you get "NaN" in the database ("Not a Number") and NaN is evaluated as
559       Unknown.
560
561       In this case, you would like to be alerted to it and the sum of the
562       remaining values is of no value to you.
563
564       It would be something like:
565
566           DEF:users1=location1.rrd:onlineTS1:LAST
567           DEF:users2=location1.rrd:onlineTS2:LAST
568           DEF:users3=location2.rrd:onlineTS1:LAST
569           DEF:users4=location2.rrd:onlineTS2:LAST
570           CDEF:allusers=users1,users2,users3,users4,+,+,+
571
572       If you now plot allusers, unknown data in one of users1..users4 will
573       show up as a gap in your graph. You want to modify this to show a
574       bright red line, not a gap.
575
576       Define an extra CDEF that is unknown if all is okay and is infinite if
577       there is an unknown value:
578
579           CDEF:wrongdata=allusers,UN,INF,UNKN,IF
580
581       "allusers,UN" will evaluate to either true or false, it is the (x) part
582       of the "IF" function and it checks if allusers is unknown.  The (y)
583       part of the "IF" function is set to "INF" (which means infinity) and
584       the (z) part of the function returns "UNKN".
585
586       The logic is: if (allusers == unknown) then return INF else return
587       UNKN.
588
589       You can now use AREA to display this "wrongdata" in bright red. If it
590       is unknown (because allusers is known) then the red AREA won't show up.
591       If the value is INF (because allusers is unknown) then the red AREA
592       will be filled in on the graph at that particular time.
593
594          AREA:allusers#0000FF:combined user count
595          AREA:wrongdata#FF0000:unknown data
596
597       Same example useful with STACKed data:
598
599       If you use stack in the previous example (as I would do) then you don't
600       add up the values. Therefore, there is no relationship between the four
601       values and you don't get a single value to test.  Suppose users3 would
602       be unknown at one point in time: users1 is plotted, users2 is stacked
603       on top of users1, users3 is unknown and therefore nothing happens,
604       users4 is stacked on top of users2.  Add the extra CDEFs anyway and use
605       them to overlay the "normal" graph:
606
607          DEF:users1=location1.rrd:onlineTS1:LAST
608          DEF:users2=location1.rrd:onlineTS2:LAST
609          DEF:users3=location2.rrd:onlineTS1:LAST
610          DEF:users4=location2.rrd:onlineTS2:LAST
611          CDEF:allusers=users1,users2,users3,users4,+,+,+
612          CDEF:wrongdata=allusers,UN,INF,UNKN,IF
613          AREA:users1#0000FF:users at ts1
614          STACK:users2#00FF00:users at ts2
615          STACK:users3#00FFFF:users at ts3
616          STACK:users4#FFFF00:users at ts4
617          AREA:wrongdata#FF0000:unknown data
618
619       If there is unknown data in one of users1..users4, the "wrongdata" AREA
620       will be drawn and because it starts at the X-axis and has infinite
621       height it will effectively overwrite the STACKed parts.
622
623       You could combine the two CDEF lines into one (we don't use "allusers")
624       if you like.  But there are good reasons for writing two CDEFS:
625
626       ·   It improves the readability of the script.
627
628       ·   It can be used inside GPRINT to display the total number of users.
629
630       If you choose to combine them, you can substitute the "allusers" in the
631       second CDEF with the part after the equal sign from the first line:
632
633          CDEF:wrongdata=users1,users2,users3,users4,+,+,+,UN,INF,UNKN,IF
634
635       If you do so, you won't be able to use these next GPRINTs:
636
637          COMMENT:"Total number of users seen"
638          GPRINT:allusers:MAX:"Maximum: %6.0lf"
639          GPRINT:allusers:MIN:"Minimum: %6.0lf"
640          GPRINT:allusers:AVERAGE:"Average: %6.0lf"
641          GPRINT:allusers:LAST:"Current: %6.0lf\n"
642

The examples from the RRD graph manual page

644       Degrees Celsius vs. Degrees Fahrenheit
645
646       To convert Celsius into Fahrenheit use the formula F=9/5*C+32
647
648          rrdtool graph demo.png --title="Demo Graph" \
649             DEF:cel=demo.rrd:exhaust:AVERAGE \
650             CDEF:far=9,5,/,cel,*,32,+ \
651             LINE2:cel#00a000:"D. Celsius" \
652             LINE2:far#ff0000:"D. Fahrenheit\c"
653
654       This example gets the DS called "exhaust" from database "demo.rrd" and
655       puts the values in variable "cel". The CDEF used is evaluated as fol‐
656       lows:
657
658          CDEF:far=9,5,/,cel,*,32,+
659          1. push 9, push 5
660          2. push function "divide" and process it
661             the stack now contains 9/5
662          3. push variable "cel"
663          4. push function "multiply" and process it
664             the stack now contains 9/5*cel
665          5. push 32
666          6. push function "plus" and process it
667             the stack contains now the temperature in Fahrenheit
668
669       Changing unknown into zero
670
671          rrdtool graph demo.png --title="Demo Graph" \
672             DEF:idat1=interface1.rrd:ds0:AVERAGE \
673             DEF:idat2=interface2.rrd:ds0:AVERAGE \
674             DEF:odat1=interface1.rrd:ds1:AVERAGE \
675             DEF:odat2=interface2.rrd:ds1:AVERAGE \
676             CDEF:agginput=idat1,UN,0,idat1,IF,idat2,UN,0,idat2,IF,+,8,* \
677             CDEF:aggoutput=odat1,UN,0,odat1,IF,odat2,UN,0,odat2,IF,+,8,* \
678             AREA:agginput#00cc00:Input Aggregate \
679             LINE1:aggoutput#0000FF:Output Aggregate
680
681       These two CDEFs are built from several functions. It helps to split
682       them when viewing what they do. Starting with the first CDEF we would
683       get:
684
685        idat1,UN --> a
686        0        --> b
687        idat1    --> c
688        if (a) then (b) else (c)
689
690       The result is therefore "0" if it is true that "idat1" equals "UN".  If
691       not, the original value of "idat1" is put back on the stack.  Lets call
692       this answer "d". The process is repeated for the next five items on the
693       stack, it is done the same and will return answer "h". The resulting
694       stack is therefore "d,h".  The expression has been simplified to
695       "d,h,+,8,*" and it will now be easy to see that we add "d" and "h", and
696       multiply the result with eight.
697
698       The end result is that we have added "idat1" and "idat2" and in the
699       process we effectively ignored unknown values. The result is multiplied
700       by eight, most likely to convert bytes/s to bits/s.
701
702       Infinity demo
703
704          rrdtool graph example.png --title="INF demo" \
705             DEF:val1=some.rrd:ds0:AVERAGE \
706             DEF:val2=some.rrd:ds1:AVERAGE \
707             DEF:val3=some.rrd:ds2:AVERAGE \
708             DEF:val4=other.rrd:ds0:AVERAGE \
709             CDEF:background=val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF \
710             CDEF:wipeout=val1,val2,val3,val4,+,+,+,UN,INF,UNKN,IF \
711             AREA:background#F0F0F0 \
712             AREA:val1#0000FF:Value1 \
713             STACK:val2#00C000:Value2 \
714             STACK:val3#FFFF00:Value3 \
715             STACK:val4#FFC000:Value4 \
716             AREA:whipeout#FF0000:Unknown
717
718       This demo demonstrates two ways to use infinity. It is a bit tricky to
719       see what happens in the "background" CDEF.
720
721          "val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF"
722
723       This RPN takes the value of "val4" as input and then immediately
724       removes it from the stack using "POP". The stack is now empty but as a
725       side effect we now know the time that this sample was taken.  This time
726       is put on the stack by the "TIME" function.
727
728       "TIME,7200,%" takes the modulo of time and 7'200 (which is two hours).
729       The resulting value on the stack will be a number in the range from 0
730       to 7199.
731
732       For people who don't know the modulo function: it is the remainder
733       after an integer division. If you divide 16 by 3, the answer would be 5
734       and the remainder would be 1. So, "16,3,%" returns 1.
735
736       We have the result of "TIME,7200,%" on the stack, lets call this "a".
737       The start of the RPN has become "a,3600,LE" and this checks if "a" is
738       less or equal than "3600". It is true half of the time.  We now have to
739       process the rest of the RPN and this is only a simple "IF" function
740       that returns either "INF" or "UNKN" depending on the time. This is
741       returned to variable "background".
742
743       The second CDEF has been discussed earlier in this document so we won't
744       do that here.
745
746       Now you can draw the different layers. Start with the background that
747       is either unknown (nothing to see) or infinite (the whole positive part
748       of the graph gets filled).
749
750       Next you draw the data on top of this background, it will overlay the
751       background. Suppose one of val1..val4 would be unknown, in that case
752       you end up with only three bars stacked on top of each other.  You
753       don't want to see this because the data is only valid when all four
754       variables are valid. This is why you use the second CDEF, it will over‐
755       lay the data with an AREA so the data cannot be seen anymore.
756
757       If your data can also have negative values you also need to overwrite
758       the other half of your graph. This can be done in a relatively simple
759       way: what you need is the "wipeout" variable and place a negative sign
760       before it:  "CDEF:wipeout2=wipeout,-1,*"
761
762       Filtering data
763
764       You may do some complex data filtering:
765
766         MEDIAN FILTER: filters shot noise
767
768           DEF:var=database.rrd:traffic:AVERAGE
769           CDEF:prev1=PREV(var)
770           CDEF:prev2=PREV(prev1)
771           CDEF:prev3=PREV(prev2)
772           CDEF:median=prev1,prev2,prev3,+,+,3,/
773           LINE3:median#000077:filtered
774           LINE1:prev2#007700:'raw data'
775
776         DERIVATE:
777
778           DEF:var=database.rrd:traffic:AVERAGE
779           CDEF:prev1=PREV(var)
780           CDEF:time=TIME
781           CDEF:prevtime=PREV(time)
782           CDEF:derivate=var,prev1,-,time,prevtime,-,/
783           LINE3:derivate#000077:derivate
784           LINE1:var#007700:'raw data'
785

Out of ideas for now

787       This document was created from questions asked by either myself or by
788       other people on the RRDtool mailing list. Please let me know if you
789       find errors in it or if you have trouble understanding it. If you think
790       there should be an addition, mail me: <alex@ergens.op.het.net>
791
792       Remember: No feedback equals no changes!
793

SEE ALSO

795       The RRDtool manpages
796

AUTHOR

798       Alex van den Bogaerdt <alex@ergens.op.het.net>
799
800
801
8021.2.27                            2008-02-17                   CDEFTUTORIAL(1)
Impressum