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