Code

Updated changelog to mention the merged NMU.
[pkg-rrdtool.git] / doc / cdeftutorial.txt
1 CDEFTUTORIAL(1)                     rrdtool                    CDEFTUTORIAL(1)
5 N\bNA\bAM\bME\bE
6        cdeftutorial - Alex van den Bogaerdt's CDEF tutorial
8 D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN
9        Intention of this document: to provide some examples of the commonly
10        used parts of RRDtool's CDEF language.
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
16        document are also welcome.  -- Alex van den Bogaerdt
17        <alex@vandenbogaerdt.nl>
19    W\bWh\bhy\by t\bth\bhi\bis\bs t\btu\but\bto\bor\bri\bia\bal\bl?\b?
20        One of the powerful parts of RRDtool is its ability to do all sorts of
21        calculations on the data retrieved from its databases. However,
22        RRDtool's many options and syntax make it difficult for the average
23        user to understand. The manuals are good at explaining what these
24        options do; however they do not (and should not) explain in detail why
25        they are useful. As with my RRDtool tutorial: if you want a simple
26        document in simple language you should read this tutorial.  If you are
27        happy with the official documentation, you may find this document too
28        simple or even boring. If you do choose to read this tutorial, I also
29        expect you to have read and fully understand my other tutorial.
31    M\bMo\bor\bre\be r\bre\bea\bad\bdi\bin\bng\bg
32        If you have difficulties with the way I try to explain it please read
33        Steve Rader's rpntutorial. It may help you understand how this all
34        works.
36 W\bWh\bha\bat\bt a\bar\bre\be C\bCD\bDE\bEF\bFs\bs?\b?
37        When retrieving data from an RRD, you are using a "DEF" to work with
38        that data. Think of it as a variable that changes over time (where time
39        is the x-axis). The value of this variable is what is found in the
40        database at that particular time and you can't do any modifications on
41        it. This is what CDEFs are for: they takes values from DEFs and perform
42        calculations on them.
44 S\bSy\byn\bnt\bta\bax\bx
45           DEF:var_name_1=some.rrd:ds_name:CF
46           CDEF:var_name_2=RPN_expression
48        You first define "var_name_1" to be data collected from data source
49        "ds_name" found in RRD "some.rrd" with consolidation function "CF".
51        Assume the ifInOctets SNMP counter is saved in mrtg.rrd as the DS "in".
52        Then the following DEF defines a variable for the average of that data
53        source:
55           DEF:inbytes=mrtg.rrd:in:AVERAGE
57        Say you want to display bits per second (instead of bytes per second as
58        stored in the database.)  You have to define a calculation (hence
59        "CDEF") on variable "inbytes" and use that variable (inbits) instead of
60        the original:
62           CDEF:inbits=inbytes,8,*
64        This tells RRDtool to multiply inbytes by eight to get inbits. I'll
65        explain later how this works. In the graphing or printing functions,
66        you can now use inbits where you would use inbytes otherwise.
68        Note that the variable name used in the CDEF (inbits) must not be the
69        same as the variable named in the DEF (inbytes)!
71 R\bRP\bPN\bN-\b-e\bex\bxp\bpr\bre\bes\bss\bsi\bio\bon\bns\bs
72        RPN is short-hand for Reverse Polish Notation. It works as follows.
73        You put the variables or numbers on a stack. You also put operations
74        (things-to-do) on the stack and this stack is then processed. The
75        result will be placed on the stack. At the end, there should be exactly
76        one number left: the outcome of the series of operations. If there is
77        not exactly one number left, RRDtool will complain loudly.
79        Above multiplication by eight will look like:
81        1.  Start with an empty stack
83        2.  Put the content of variable inbytes on the stack
85        3.  Put the number eight on the stack
87        4.  Put the operation multiply on the stack
89        5.  Process the stack
91        6.  Retrieve the value from the stack and put it in variable inbits
93        We will now do an example with real numbers. Suppose the variable
94        inbytes would have value 10, the stack would be:
96        1.  ||
98        2.  |10|
100        3.  |10|8|
102        4.  |10|8|*|
104        5.  |80|
106        6.  ||
108        Processing the stack (step 5) will retrieve one value from the stack
109        (from the right at step 4). This is the operation multiply and this
110        takes two values off the stack as input. The result is put back on the
111        stack (the value 80 in this case). For multiplication the order doesn't
112        matter, but for other operations like subtraction and division it does.
113        Generally speaking you have the following order:
115           y = A - B  -->  y=minus(A,B)  -->  CDEF:y=A,B,-
117        This is not very intuitive (at least most people don't think so). For
118        the function f(A,B) you reverse the position of "f", but you do not
119        reverse the order of the variables.
121 C\bCo\bon\bnv\bve\ber\brt\bti\bin\bng\bg y\byo\bou\bur\br w\bwi\bis\bsh\bhe\bes\bs t\bto\bo R\bRP\bPN\bN
122        First, get a clear picture of what you want to do. Break down the
123        problem in smaller portions until they cannot be split anymore. Then it
124        is rather simple to convert your ideas into RPN.
126        Suppose you have several RRDs and would like to add up some counters in
127        them. These could be, for instance, the counters for every WAN link you
128        are monitoring.
130        You have:
132           router1.rrd with link1in link2in
133           router2.rrd with link1in link2in
134           router3.rrd with link1in link2in
136        Suppose you would like to add up all these counters, except for link2in
137        inside router2.rrd. You need to do:
139        (in this example, "router1.rrd:link1in" means the DS link1in inside the
140        RRD router1.rrd)
142           router1.rrd:link1in
143           router1.rrd:link2in
144           router2.rrd:link1in
145           router3.rrd:link1in
146           router3.rrd:link2in
147           --------------------   +
148           (outcome of the sum)
150        As a mathematical function, this could be written:
152        "add(router1.rrd:link1in , router1.rrd:link2in , router2.rrd:link1in ,
153        router3.rrd:link1in , router3.rrd:link2.in)"
155        With RRDtool and RPN, first, define the inputs:
157           DEF:a=router1.rrd:link1in:AVERAGE
158           DEF:b=router1.rrd:link2in:AVERAGE
159           DEF:c=router2.rrd:link1in:AVERAGE
160           DEF:d=router3.rrd:link1in:AVERAGE
161           DEF:e=router3.rrd:link2in:AVERAGE
163        Now, the mathematical function becomes: "add(a,b,c,d,e)"
165        In RPN, there's no operator that sums more than two values so you need
166        to do several additions. You add a and b, add c to the result, add d to
167        the result and add e to the result.
169           push a:         a     stack contains the value of a
170           push b and add: b,+   stack contains the result of a+b
171           push c and add: c,+   stack contains the result of a+b+c
172           push d and add: d,+   stack contains the result of a+b+c+d
173           push e and add: e,+   stack contains the result of a+b+c+d+e
175        What was calculated here would be written down as:
177           ( ( ( (a+b) + c) + d) + e) >
179        This is in RPN:  "CDEF:result=a,b,+,c,+,d,+,e,+"
181        This is correct but it can be made more clear to humans. It does not
182        matter if you add a to b and then add c to the result or first add b to
183        c and then add a to the result. This makes it possible to rewrite the
184        RPN into "CDEF:result=a,b,c,d,e,+,+,+,+" which is evaluated
185        differently:
187           push value of variable a on the stack: a
188           push value of variable b on the stack: a b
189           push value of variable c on the stack: a b c
190           push value of variable d on the stack: a b c d
191           push value of variable e on the stack: a b c d e
192           push operator + on the stack:          a b c d e +
193           and process it:                        a b c P   (where P == d+e)
194           push operator + on the stack:          a b c P +
195           and process it:                        a b Q     (where Q == c+P)
196           push operator + on the stack:          a b Q +
197           and process it:                        a R       (where R == b+Q)
198           push operator + on the stack:          a R +
199           and process it:                        S         (where S == a+R)
201        As you can see the RPN expression "a,b,c,d,e,+,+,+,+,+" will evaluate
202        in "((((d+e)+c)+b)+a)" and it has the same outcome as
203        "a,b,+,c,+,d,+,e,+".  This is called the commutative law of addition,
204        but you may forget this right away, as long as you remember what it
205        means.
207        Now look at an expression that contains a multiplication:
209        First in normal math: "let result = a+b*c". In this case you can't
210        choose the order yourself, you have to start with the multiplication
211        and then add a to it. You may alter the position of b and c, you must
212        not alter the position of a and b.
214        You have to take this in consideration when converting this expression
215        into RPN. Read it as: "Add the outcome of b*c to a" and then it is easy
216        to write the RPN expression: "result=a,b,c,*,+" Another expression that
217        would return the same: "result=b,c,*,a,+"
219        In normal math, you may encounter something like "a*(b+c)" and this can
220        also be converted into RPN. The parenthesis just tell you to first add
221        b and c, and then multiply a with the result. Again, now it is easy to
222        write it in RPN: "result=a,b,c,+,*". Note that this is very similar to
223        one of the expressions in the previous paragraph, only the
224        multiplication and the addition changed places.
226        When you have problems with RPN or when RRDtool is complaining, it's
227        usually a good thing to write down the stack on a piece of paper and
228        see what happens. Have the manual ready and pretend to be RRDtool.
229        Just do all the math by hand to see what happens, I'm sure this will
230        solve most, if not all, problems you encounter.
232 S\bSo\bom\bme\be s\bsp\bpe\bec\bci\bia\bal\bl n\bnu\bum\bmb\bbe\ber\brs\bs
233    T\bTh\bhe\be u\bun\bnk\bkn\bno\bow\bwn\bn v\bva\bal\blu\bue\be
234        Sometimes collecting your data will fail. This can be very common,
235        especially when querying over busy links. RRDtool can be configured to
236        allow for one (or even more) unknown value(s) and calculate the missing
237        update. You can, for instance, query your device every minute. This is
238        creating one so called PDP or primary data point per minute. If you
239        defined your RRD to contain an RRA that stores 5-minute values, you
240        need five of those PDPs to create one CDP (consolidated data point).
241        These PDPs can become unknown in two cases:
243        1.  The updates are too far apart. This is tuned using the "heartbeat"
244            setting.
246        2.  The update was set to unknown on purpose by inserting no value
247            (using the template option) or by using "U" as the value to insert.
249        When a CDP is calculated, another mechanism determines if this CDP is
250        valid or not. If there are too many PDPs unknown, the CDP is unknown as
251        well.  This is determined by the xff factor. Please note that one
252        unknown counter update can result in two unknown PDPs! If you only
253        allow for one unknown PDP per CDP, this makes the CDP go unknown!
255        Suppose the counter increments with one per second and you retrieve it
256        every minute:
258           counter value    resulting rate
259           10'000
260           10'060            1; (10'060-10'000)/60 == 1
261           10'120            1; (10'120-10'060)/60 == 1
262           unknown           unknown; you don't know the last value
263           10'240            unknown; you don't know the previous value
264           10'300            1; (10'300-10'240)/60 == 1
266        If the CDP was to be calculated from the last five updates, it would
267        get two unknown PDPs and three known PDPs. If xff would have been set
268        to 0.5 which by the way is a commonly used factor, the CDP would have a
269        known value of 1. If xff would have been set to 0.2 then the resulting
270        CDP would be unknown.
272        You have to decide the proper values for heartbeat, number of PDPs per
273        CDP and the xff factor. As you can see from the previous text they
274        define the behavior of your RRA.
276    W\bWo\bor\brk\bki\bin\bng\bg w\bwi\bit\bth\bh u\bun\bnk\bkn\bno\bow\bwn\bn d\bda\bat\bta\ba i\bin\bn y\byo\bou\bur\br d\bda\bat\bta\bab\bba\bas\bse\be
277        As you have read in the previous chapter, entries in an RRA can be set
278        to the unknown value. If you do calculations with this type of value,
279        the result has to be unknown too. This means that an expression such as
280        "result=a,b,+" will be unknown if either a or b is unknown.  It would
281        be wrong to just ignore the unknown value and return the value of the
282        other parameter. By doing so, you would assume "unknown" means "zero"
283        and this is not true.
285        There has been a case where somebody was collecting data for over a
286        year.  A new piece of equipment was installed, a new RRD was created
287        and the scripts were changed to add a counter from the old database and
288        a counter from the new database. The result was disappointing, a large
289        part of the statistics seemed to have vanished mysteriously ...  They
290        of course didn't, values from the old database (known values) were
291        added to values from the new database (unknown values) and the result
292        was unknown.
294        In this case, it is fairly reasonable to use a CDEF that alters unknown
295        data into zero. The counters of the device were unknown (after all, it
296        wasn't installed yet!) but you know that the data rate through the
297        device had to be zero (because of the same reason: it was not
298        installed).
300        There are some examples below that make this change.
302    I\bIn\bnf\bfi\bin\bni\bit\bty\by
303        Infinite data is another form of a special number. It cannot be graphed
304        because by definition you would never reach the infinite value. You can
305        think of positive and negative infinity depending on the position
306        relative to zero.
308        RRDtool is capable of representing (-not- graphing!) infinity by
309        stopping at its current maximum (for positive infinity) or minimum (for
310        negative infinity) without knowing this maximum (minimum).
312        Infinity in RRDtool is mostly used to draw an AREA without knowing its
313        vertical dimensions. You can think of it as drawing an AREA with an
314        infinite height and displaying only the part that is visible in the
315        current graph. This is probably a good way to approximate infinity and
316        it sure allows for some neat tricks. See below for examples.
318    W\bWo\bor\brk\bki\bin\bng\bg w\bwi\bit\bth\bh u\bun\bnk\bkn\bno\bow\bwn\bn d\bda\bat\bta\ba a\ban\bnd\bd i\bin\bnf\bfi\bin\bni\bit\bty\by
319        Sometimes you would like to discard unknown data and pretend it is zero
320        (or any other value for that matter) and sometimes you would like to
321        pretend that known data is unknown (to discard known-to-be-wrong data).
322        This is why CDEFs have support for unknown data. There are also
323        examples available that show unknown data by using infinity.
325 S\bSo\bom\bme\be e\bex\bxa\bam\bmp\bpl\ble\bes\bs
326    E\bEx\bxa\bam\bmp\bpl\ble\be:\b: u\bus\bsi\bin\bng\bg a\ba r\bre\bec\bce\ben\bnt\btl\bly\by c\bcr\bre\bea\bat\bte\bed\bd R\bRR\bRD\bD
327        You are keeping statistics on your router for over a year now. Recently
328        you installed an extra router and you would like to show the combined
329        throughput for these two devices.
331        If you just add up the counters from router.rrd and router2.rrd, you
332        will add known data (from router.rrd) to unknown data (from
333        router2.rrd) for the bigger part of your stats. You could solve this in
334        a few ways:
336        ·   While creating the new database, fill it with zeros from the start
337            to now.  You have to make the database start at or before the least
338            recent time in the other database.
340        ·   Alternatively, you could use CDEF and alter unknown data to zero.
342        Both methods have their pros and cons. The first method is troublesome
343        and if you want to do that you have to figure it out yourself. It is
344        not possible to create a database filled with zeros, you have to put
345        them in manually. Implementing the second method is described next:
347        What we want is: "if the value is unknown, replace it with zero". This
348        could be written in pseudo-code as:  if (value is unknown) then (zero)
349        else (value). When reading the rrdgraph manual you notice the "UN"
350        function that returns zero or one. You also notice the "IF" function
351        that takes zero or one as input.
353        First look at the "IF" function. It takes three values from the stack,
354        the first value is the decision point, the second value is returned to
355        the stack if the evaluation is "true" and if not, the third value is
356        returned to the stack. We want the "UN" function to decide what happens
357        so we combine those two functions in one CDEF.
359        Lets write down the two possible paths for the "IF" function:
361           if true  return a
362           if false return b
364        In RPN:  "result=x,a,b,IF" where "x" is either true or false.
366        Now we have to fill in "x", this should be the "(value is unknown)"
367        part and this is in RPN:  "result=value,UN"
369        We now combine them: "result=value,UN,a,b,IF" and when we fill in the
370        appropriate things for "a" and "b" we're finished:
372        "CDEF:result=value,UN,0,value,IF"
374        You may want to read Steve Rader's RPN guide if you have difficulties
375        with the way I explained this last example.
377        If you want to check this RPN expression, just mimic RRDtool behavior:
379           For any known value, the expression evaluates as follows:
380           CDEF:result=value,UN,0,value,IF  (value,UN) is not true so it becomes 0
381           CDEF:result=0,0,value,IF         "IF" will return the 3rd value
382           CDEF:result=value                The known value is returned
384           For the unknown value, this happens:
385           CDEF:result=value,UN,0,value,IF  (value,UN) is true so it becomes 1
386           CDEF:result=1,0,value,IF         "IF" sees 1 and returns the 2nd value
387           CDEF:result=0                    Zero is returned
389        Of course, if you would like to see another value instead of zero, you
390        can use that other value.
392        Eventually, when all unknown data is removed from the RRD, you may want
393        to remove this rule so that unknown data is properly displayed.
395    E\bEx\bxa\bam\bmp\bpl\ble\be:\b: b\bbe\bet\btt\bte\ber\br h\bha\ban\bnd\bdl\bli\bin\bng\bg o\bof\bf u\bun\bnk\bkn\bno\bow\bwn\bn d\bda\bat\bta\ba,\b, b\bby\by u\bus\bsi\bin\bng\bg t\bti\bim\bme\be
396        The above example has one drawback. If you do log unknown data in your
397        database after installing your new equipment, it will also be
398        translated into zero and therefore you won't see that there was a
399        problem. This is not good and what you really want to do is:
401        ·   If there is unknown data, look at the time that this sample was
402            taken.
404        ·   If the unknown value is before time xxx, make it zero.
406        ·   If it is after time xxx, leave it as unknown data.
408        This is doable: you can compare the time that the sample was taken to
409        some known time. Assuming you started to monitor your device on Friday
410        September 17, 1999, 00:35:57 MET DST. Translate this time in seconds
411        since 1970-01-01 and it becomes 937'521'357. If you process unknown
412        values that were received after this time, you want to leave them
413        unknown and if they were "received" before this time, you want to
414        translate them into zero (so you can effectively ignore them while
415        adding them to your other routers counters).
417        Translating Friday September 17, 1999, 00:35:57 MET DST into
418        937'521'357 can be done by, for instance, using gnu date:
420           date -d "19990917 00:35:57" +%s
422        You could also dump the database and see where the data starts to be
423        known. There are several other ways of doing this, just pick one.
425        Now we have to create the magic that allows us to process unknown
426        values different depending on the time that the sample was taken.  This
427        is a three step process:
429        1.  If the timestamp of the value is after 937'521'357, leave it as is.
431        2.  If the value is a known value, leave it as is.
433        3.  Change the unknown value into zero.
435        Lets look at part one:
437            if (true) return the original value
439        We rewrite this:
441            if (true) return "a"
442            if (false) return "b"
444        We need to calculate true or false from step 1. There is a function
445        available that returns the timestamp for the current sample. It is
446        called, how surprisingly, "TIME". This time has to be compared to a
447        constant number, we need "GT". The output of "GT" is true or false and
448        this is good input to "IF". We want "if (time > 937521357) then (return
449        a) else (return b)".
451        This process was already described thoroughly in the previous chapter
452        so lets do it quick:
454           if (x) then a else b
455              where x represents "time>937521357"
456              where a represents the original value
457              where b represents the outcome of the previous example
459           time>937521357       --> TIME,937521357,GT
461           if (x) then a else b --> x,a,b,IF
462           substitute x         --> TIME,937521357,GT,a,b,IF
463           substitute a         --> TIME,937521357,GT,value,b,IF
464           substitute b         --> TIME,937521357,GT,value,value,UN,0,value,IF,IF
466        We end up with:
467        "CDEF:result=TIME,937521357,GT,value,value,UN,0,value,IF,IF"
469        This looks very complex, however, as you can see, it was not too hard
470        to come up with.
472    E\bEx\bxa\bam\bmp\bpl\ble\be:\b: P\bPr\bre\bet\bte\ben\bnd\bdi\bin\bng\bg w\bwe\bei\bir\brd\bd d\bda\bat\bta\ba i\bis\bsn\bn'\b't\bt t\bth\bhe\ber\bre\be
473        Suppose you have a problem that shows up as huge spikes in your graph.
474        You know this happens and why, so you decide to work around the
475        problem.  Perhaps you're using your network to do a backup at night and
476        by doing so you get almost 10mb/s while the rest of your network
477        activity does not produce numbers higher than 100kb/s.
479        There are two options:
481        1.  If the number exceeds 100kb/s it is wrong and you want it masked
482            out by changing it into unknown.
484        2.  You don't want the graph to show more than 100kb/s.
486        Pseudo code: if (number > 100) then unknown else number or Pseudo code:
487        if (number > 100) then 100 else number.
489        The second "problem" may also be solved by using the rigid option of
490        RRDtool graph, however this has not the same result. In this example
491        you can end up with a graph that does autoscaling. Also, if you use the
492        numbers to display maxima they will be set to 100kb/s.
494        We use "IF" and "GT" again. "if (x) then (y) else (z)" is written down
495        as "CDEF:result=x,y,z,IF"; now fill in x, y and z.  For x you fill in
496        "number greater than 100kb/s" becoming "number,100000,GT" (kilo is
497        1'000 and b/s is what we measure!).  The "z" part is "number" in both
498        cases and the "y" part is either "UNKN" for unknown or "100000" for
499        100kb/s.
501        The two CDEF expressions would be:
503            CDEF:result=number,100000,GT,UNKN,number,IF
504            CDEF:result=number,100000,GT,100000,number,IF
506    E\bEx\bxa\bam\bmp\bpl\ble\be:\b: w\bwo\bor\brk\bki\bin\bng\bg o\bon\bn a\ba c\bce\ber\brt\bta\bai\bin\bn t\bti\bim\bme\be s\bsp\bpa\ban\bn
507        If you want a graph that spans a few weeks, but would only want to see
508        some routers' data for one week, you need to "hide" the rest of the
509        time frame. Don't ask me when this would be useful, it's just here for
510        the example :)
512        We need to compare the time stamp to a begin date and an end date.
513        Comparing isn't difficult:
515                TIME,begintime,GE
516                TIME,endtime,LE
518        These two parts of the CDEF produce either 0 for false or 1 for true.
519        We can now check if they are both 0 (or 1) using a few IF statements
520        but, as Wataru Satoh pointed out, we can use the "*" or "+" functions
521        as logical AND and logical OR.
523        For "*", the result will be zero (false) if either one of the two
524        operators is zero.  For "+", the result will only be false (0) when two
525        false (0) operators will be added.  Warning: *any* number not equal to
526        0 will be considered "true". This means that, for instance, "-1,1,+"
527        (which should be "true or true") will become FALSE ...  In other words,
528        use "+" only if you know for sure that you have positive numbers (or
529        zero) only.
531        Let's compile the complete CDEF:
533                DEF:ds0=router1.rrd:AVERAGE
534                CDEF:ds0modified=TIME,begintime,GT,TIME,endtime,LE,*,ds0,UNKN,IF
536        This will return the value of ds0 if both comparisons return true. You
537        could also do it the other way around:
539                DEF:ds0=router1.rrd:AVERAGE
540                CDEF:ds0modified=TIME,begintime,LT,TIME,endtime,GT,+,UNKN,ds0,IF
542        This will return an UNKNOWN if either comparison returns true.
544    E\bEx\bxa\bam\bmp\bpl\ble\be:\b: Y\bYo\bou\bu s\bsu\bus\bsp\bpe\bec\bct\bt t\bto\bo h\bha\bav\bve\be p\bpr\bro\bob\bbl\ble\bem\bms\bs a\ban\bnd\bd w\bwa\ban\bnt\bt t\bto\bo s\bse\bee\be u\bun\bnk\bkn\bno\bow\bwn\bn d\bda\bat\bta\ba.\b.
545        Suppose you add up the number of active users on several terminal
546        servers.  If one of them doesn't give an answer (or an incorrect one)
547        you get "NaN" in the database ("Not a Number") and NaN is evaluated as
548        Unknown.
550        In this case, you would like to be alerted to it and the sum of the
551        remaining values is of no value to you.
553        It would be something like:
555            DEF:users1=location1.rrd:onlineTS1:LAST
556            DEF:users2=location1.rrd:onlineTS2:LAST
557            DEF:users3=location2.rrd:onlineTS1:LAST
558            DEF:users4=location2.rrd:onlineTS2:LAST
559            CDEF:allusers=users1,users2,users3,users4,+,+,+
561        If you now plot allusers, unknown data in one of users1..users4 will
562        show up as a gap in your graph. You want to modify this to show a
563        bright red line, not a gap.
565        Define an extra CDEF that is unknown if all is okay and is infinite if
566        there is an unknown value:
568            CDEF:wrongdata=allusers,UN,INF,UNKN,IF
570        "allusers,UN" will evaluate to either true or false, it is the (x) part
571        of the "IF" function and it checks if allusers is unknown.  The (y)
572        part of the "IF" function is set to "INF" (which means infinity) and
573        the (z) part of the function returns "UNKN".
575        The logic is: if (allusers == unknown) then return INF else return
576        UNKN.
578        You can now use AREA to display this "wrongdata" in bright red. If it
579        is unknown (because allusers is known) then the red AREA won't show up.
580        If the value is INF (because allusers is unknown) then the red AREA
581        will be filled in on the graph at that particular time.
583           AREA:allusers#0000FF:combined user count
584           AREA:wrongdata#FF0000:unknown data
586    S\bSa\bam\bme\be e\bex\bxa\bam\bmp\bpl\ble\be u\bus\bse\bef\bfu\bul\bl w\bwi\bit\bth\bh S\bST\bTA\bAC\bCK\bKe\bed\bd d\bda\bat\bta\ba:\b:
587        If you use stack in the previous example (as I would do) then you don't
588        add up the values. Therefore, there is no relationship between the four
589        values and you don't get a single value to test.  Suppose users3 would
590        be unknown at one point in time: users1 is plotted, users2 is stacked
591        on top of users1, users3 is unknown and therefore nothing happens,
592        users4 is stacked on top of users2.  Add the extra CDEFs anyway and use
593        them to overlay the "normal" graph:
595           DEF:users1=location1.rrd:onlineTS1:LAST
596           DEF:users2=location1.rrd:onlineTS2:LAST
597           DEF:users3=location2.rrd:onlineTS1:LAST
598           DEF:users4=location2.rrd:onlineTS2:LAST
599           CDEF:allusers=users1,users2,users3,users4,+,+,+
600           CDEF:wrongdata=allusers,UN,INF,UNKN,IF
601           AREA:users1#0000FF:users at ts1
602           STACK:users2#00FF00:users at ts2
603           STACK:users3#00FFFF:users at ts3
604           STACK:users4#FFFF00:users at ts4
605           AREA:wrongdata#FF0000:unknown data
607        If there is unknown data in one of users1..users4, the "wrongdata" AREA
608        will be drawn and because it starts at the X-axis and has infinite
609        height it will effectively overwrite the STACKed parts.
611        You could combine the two CDEF lines into one (we don't use "allusers")
612        if you like.  But there are good reasons for writing two CDEFS:
614        ·   It improves the readability of the script.
616        ·   It can be used inside GPRINT to display the total number of users.
618        If you choose to combine them, you can substitute the "allusers" in the
619        second CDEF with the part after the equal sign from the first line:
621           CDEF:wrongdata=users1,users2,users3,users4,+,+,+,UN,INF,UNKN,IF
623        If you do so, you won't be able to use these next GPRINTs:
625           COMMENT:"Total number of users seen"
626           GPRINT:allusers:MAX:"Maximum: %6.0lf"
627           GPRINT:allusers:MIN:"Minimum: %6.0lf"
628           GPRINT:allusers:AVERAGE:"Average: %6.0lf"
629           GPRINT:allusers:LAST:"Current: %6.0lf\n"
631 T\bTh\bhe\be e\bex\bxa\bam\bmp\bpl\ble\bes\bs f\bfr\bro\bom\bm t\bth\bhe\be R\bRR\bRD\bD g\bgr\bra\bap\bph\bh m\bma\ban\bnu\bua\bal\bl p\bpa\bag\bge\be
632    D\bDe\beg\bgr\bre\bee\bes\bs C\bCe\bel\bls\bsi\biu\bus\bs v\bvs\bs.\b. D\bDe\beg\bgr\bre\bee\bes\bs F\bFa\bah\bhr\bre\ben\bnh\bhe\bei\bit\bt
633        To convert Celsius into Fahrenheit use the formula F=9/5*C+32
635           rrdtool graph demo.png --title="Demo Graph" \
636              DEF:cel=demo.rrd:exhaust:AVERAGE \
637              CDEF:far=9,5,/,cel,*,32,+ \
638              LINE2:cel#00a000:"D. Celsius" \
639              LINE2:far#ff0000:"D. Fahrenheit\c"
641        This example gets the DS called "exhaust" from database "demo.rrd" and
642        puts the values in variable "cel". The CDEF used is evaluated as
643        follows:
645           CDEF:far=9,5,/,cel,*,32,+
646           1. push 9, push 5
647           2. push function "divide" and process it
648              the stack now contains 9/5
649           3. push variable "cel"
650           4. push function "multiply" and process it
651              the stack now contains 9/5*cel
652           5. push 32
653           6. push function "plus" and process it
654              the stack contains now the temperature in Fahrenheit
656    C\bCh\bha\ban\bng\bgi\bin\bng\bg u\bun\bnk\bkn\bno\bow\bwn\bn i\bin\bnt\bto\bo z\bze\ber\bro\bo
657           rrdtool graph demo.png --title="Demo Graph" \
658              DEF:idat1=interface1.rrd:ds0:AVERAGE \
659              DEF:idat2=interface2.rrd:ds0:AVERAGE \
660              DEF:odat1=interface1.rrd:ds1:AVERAGE \
661              DEF:odat2=interface2.rrd:ds1:AVERAGE \
662              CDEF:agginput=idat1,UN,0,idat1,IF,idat2,UN,0,idat2,IF,+,8,* \
663              CDEF:aggoutput=odat1,UN,0,odat1,IF,odat2,UN,0,odat2,IF,+,8,* \
664              AREA:agginput#00cc00:Input Aggregate \
665              LINE1:aggoutput#0000FF:Output Aggregate
667        These two CDEFs are built from several functions. It helps to split
668        them when viewing what they do. Starting with the first CDEF we would
669        get:
671         idat1,UN --> a
672         0        --> b
673         idat1    --> c
674         if (a) then (b) else (c)
676        The result is therefore "0" if it is true that "idat1" equals "UN".  If
677        not, the original value of "idat1" is put back on the stack.  Lets call
678        this answer "d". The process is repeated for the next five items on the
679        stack, it is done the same and will return answer "h". The resulting
680        stack is therefore "d,h".  The expression has been simplified to
681        "d,h,+,8,*" and it will now be easy to see that we add "d" and "h", and
682        multiply the result with eight.
684        The end result is that we have added "idat1" and "idat2" and in the
685        process we effectively ignored unknown values. The result is multiplied
686        by eight, most likely to convert bytes/s to bits/s.
688    I\bIn\bnf\bfi\bin\bni\bit\bty\by d\bde\bem\bmo\bo
689           rrdtool graph example.png --title="INF demo" \
690              DEF:val1=some.rrd:ds0:AVERAGE \
691              DEF:val2=some.rrd:ds1:AVERAGE \
692              DEF:val3=some.rrd:ds2:AVERAGE \
693              DEF:val4=other.rrd:ds0:AVERAGE \
694              CDEF:background=val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF \
695              CDEF:wipeout=val1,val2,val3,val4,+,+,+,UN,INF,UNKN,IF \
696              AREA:background#F0F0F0 \
697              AREA:val1#0000FF:Value1 \
698              STACK:val2#00C000:Value2 \
699              STACK:val3#FFFF00:Value3 \
700              STACK:val4#FFC000:Value4 \
701              AREA:whipeout#FF0000:Unknown
703        This demo demonstrates two ways to use infinity. It is a bit tricky to
704        see what happens in the "background" CDEF.
706           "val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF"
708        This RPN takes the value of "val4" as input and then immediately
709        removes it from the stack using "POP". The stack is now empty but as a
710        side effect we now know the time that this sample was taken.  This time
711        is put on the stack by the "TIME" function.
713        "TIME,7200,%" takes the modulo of time and 7'200 (which is two hours).
714        The resulting value on the stack will be a number in the range from 0
715        to 7199.
717        For people who don't know the modulo function: it is the remainder
718        after an integer division. If you divide 16 by 3, the answer would be 5
719        and the remainder would be 1. So, "16,3,%" returns 1.
721        We have the result of "TIME,7200,%" on the stack, lets call this "a".
722        The start of the RPN has become "a,3600,LE" and this checks if "a" is
723        less or equal than "3600". It is true half of the time.  We now have to
724        process the rest of the RPN and this is only a simple "IF" function
725        that returns either "INF" or "UNKN" depending on the time. This is
726        returned to variable "background".
728        The second CDEF has been discussed earlier in this document so we won't
729        do that here.
731        Now you can draw the different layers. Start with the background that
732        is either unknown (nothing to see) or infinite (the whole positive part
733        of the graph gets filled).
735        Next you draw the data on top of this background, it will overlay the
736        background. Suppose one of val1..val4 would be unknown, in that case
737        you end up with only three bars stacked on top of each other.  You
738        don't want to see this because the data is only valid when all four
739        variables are valid. This is why you use the second CDEF, it will
740        overlay the data with an AREA so the data cannot be seen anymore.
742        If your data can also have negative values you also need to overwrite
743        the other half of your graph. This can be done in a relatively simple
744        way: what you need is the "wipeout" variable and place a negative sign
745        before it:  "CDEF:wipeout2=wipeout,-1,*"
747    F\bFi\bil\blt\bte\ber\bri\bin\bng\bg d\bda\bat\bta\ba
748        You may do some complex data filtering:
750          MEDIAN FILTER: filters shot noise
752            DEF:var=database.rrd:traffic:AVERAGE
753            CDEF:prev1=PREV(var)
754            CDEF:prev2=PREV(prev1)
755            CDEF:prev3=PREV(prev2)
756            CDEF:median=prev1,prev2,prev3,+,+,3,/
757            LINE3:median#000077:filtered
758            LINE1:prev2#007700:'raw data'
761          DERIVATE:
763            DEF:var=database.rrd:traffic:AVERAGE
764            CDEF:prev1=PREV(var)
765            CDEF:time=var,POP,TIME
766            CDEF:prevtime=PREV(time)
767            CDEF:derivate=var,prev1,-,time,prevtime,-,/
768            LINE3:derivate#000077:derivate
769            LINE1:var#007700:'raw data'
771 O\bOu\but\bt o\bof\bf i\bid\bde\bea\bas\bs f\bfo\bor\br n\bno\bow\bw
772        This document was created from questions asked by either myself or by
773        other people on the RRDtool mailing list. Please let me know if you
774        find errors in it or if you have trouble understanding it. If you think
775        there should be an addition, mail me: <alex@vandenbogaerdt.nl>
777        Remember: N\bNo\bo f\bfe\bee\bed\bdb\bba\bac\bck\bk e\beq\bqu\bua\bal\bls\bs n\bno\bo c\bch\bha\ban\bng\bge\bes\bs!\b!
779 S\bSE\bEE\bE A\bAL\bLS\bSO\bO
780        The RRDtool manpages
782 A\bAU\bUT\bTH\bHO\bOR\bR
783        Alex van den Bogaerdt <alex@vandenbogaerdt.nl>
787 1.4.7                             2010-05-10                   CDEFTUTORIAL(1)