Code

c67ecaa0998a6884d6402220d829536dda137a0b
[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 docu-
16        ment are also welcome.  -- Alex van den Bogaerdt <alex@vandenbo-
17        gaerdt.nl>
19        W\bWh\bhy\by t\bth\bhi\bis\bs t\btu\but\bto\bor\bri\bia\bal\bl?\b?
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.
32        M\bMo\bor\bre\be r\bre\bea\bad\bdi\bin\bng\bg
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.
38 W\bWh\bha\bat\bt a\bar\bre\be C\bCD\bDE\bEF\bFs\bs?\b?
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.
46 S\bSy\byn\bnt\bta\bax\bx
47           DEF:var_name_1=some.rrd:ds_name:CF
48           CDEF:var_name_2=RPN_expression
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".
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:
57           DEF:inbytes=mrtg.rrd:in:AVERAGE
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:
64           CDEF:inbits=inbytes,8,*
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.
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)!
73 R\bRP\bPN\bN-\b-e\bex\bxp\bpr\bre\bes\bss\bsi\bio\bon\bns\bs
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.
81        Above multiplication by eight will look like:
83        1.  Start with an empty stack
85        2.  Put the content of variable inbytes on the stack
87        3.  Put the number eight on the stack
89        4.  Put the operation multiply on the stack
91        5.  Process the stack
93        6.  Retrieve the value from the stack and put it in variable inbits
95        We will now do an example with real numbers. Suppose the variable
96        inbytes would have value 10, the stack would be:
98        1.  ||
100        2.  |10|
102        3.  |10|8|
104        4.  |10|8|*|
106        5.  |80|
108        6.  ||
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:
117           y = A - B  -->  y=minus(A,B)  -->  CDEF:y=A,B,-
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.
123 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
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.
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.
132        You have:
134           router1.rrd with link1in link2in
135           router2.rrd with link1in link2in
136           router3.rrd with link1in link2in
138        Suppose you would like to add up all these counters, except for link2in
139        inside router2.rrd. You need to do:
141        (in this example, "router1.rrd:link1in" means the DS link1in inside the
142        RRD router1.rrd)
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)
152        As a mathematical function, this could be written:
154        "add(router1.rrd:link1in , router1.rrd:link2in , router2.rrd:link1in ,
155        router3.rrd:link1in , router3.rrd:link2.in)"
157        With RRDtool and RPN, first, define the inputs:
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
165        Now, the mathematical function becomes: "add(a,b,c,d,e)"
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.
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
177        What was calculated here would be written down as:
179           ( ( ( (a+b) + c) + d) + e) >
181        This is in RPN:  "CDEF:result=a,b,+,c,+,d,+,e,+"
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:
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)
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.
209        Now look at an expression that contains a multiplication:
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.
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,+"
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.
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.
234 S\bSo\bom\bme\be s\bsp\bpe\bec\bci\bia\bal\bl n\bnu\bum\bmb\bbe\ber\brs\bs
235        T\bTh\bhe\be u\bun\bnk\bkn\bno\bow\bwn\bn v\bva\bal\blu\bue\be
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:
246        1.  The updates are too far apart. This is tuned using the "heartbeat"
247            setting.
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.
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!
258        Suppose the counter increments with one per second and you retrieve it
259        every minute:
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
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.
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.
279        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
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.
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.
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).
304        There are some examples below that make this change.
306        I\bIn\bnf\bfi\bin\bni\bit\bty\by
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.
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).
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.
323        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
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.
331 S\bSo\bom\bme\be e\bex\bxa\bam\bmp\bpl\ble\bes\bs
332        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
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.
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:
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.
347        ·   Alternatively, you could use CDEF and alter unknown data to zero.
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:
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.
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.
366        Lets write down the two possible paths for the "IF" function:
368           if true  return a
369           if false return b
371        In RPN:  "result=x,a,b,IF" where "x" is either true or false.
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"
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:
379        "CDEF:result=value,UN,0,value,IF"
381        You may want to read Steve Rader's RPN guide if you have difficulties
382        with the way I explained this last example.
384        If you want to check this RPN expression, just mimic RRDtool behavior:
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
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
396        Of course, if you would like to see another value instead of zero, you
397        can use that other value.
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.
402        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
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:
409        ·   If there is unknown data, look at the time that this sample was
410            taken.
412        ·   If the unknown value is before time xxx, make it zero.
414        ·   If it is after time xxx, leave it as unknown data.
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).
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:
428           date -d "19990917 00:35:57" +%s
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.
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:
437        1.  If the timestamp of the value is after 937'521'357, leave it as is.
439        2.  If the value is a known value, leave it as is.
441        3.  Change the unknown value into zero.
443        Lets look at part one:
445            if (true) return the original value
447        We rewrite this:
449            if (true) return "a"
450            if (false) return "b"
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)".
459        This process was already described thoroughly in the previous chapter
460        so lets do it quick:
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
467           time>937521357       --> TIME,937521357,GT
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
474        We end up with:
475        "CDEF:result=TIME,937521357,GT,value,value,UN,0,value,IF,IF"
477        This looks very complex, however, as you can see, it was not too hard
478        to come up with.
480        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
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.
488        There are two options:
490        1.  If the number exceeds 100kb/s it is wrong and you want it masked
491            out by changing it into unknown.
493        2.  You don't want the graph to show more than 100kb/s.
495        Pseudo code: if (number > 100) then unknown else number or Pseudo code:
496        if (number > 100) then 100 else number.
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.
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.
510        The two CDEF expressions would be:
512            CDEF:result=number,100000,GT,UNKN,number,IF
513            CDEF:result=number,100000,GT,100000,number,IF
515        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
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 :)
522        We need to compare the time stamp to a begin date and an end date.
523        Comparing isn't difficult:
525                TIME,begintime,GE
526                TIME,endtime,LE
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.
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.
541        Let's compile the complete CDEF:
543                DEF:ds0=router1.rrd:AVERAGE
544                CDEF:ds0modified=TIME,begintime,GT,TIME,endtime,LE,*,ds0,UNKN,IF
546        This will return the value of ds0 if both comparisons return true. You
547        could also do it the other way around:
549                DEF:ds0=router1.rrd:AVERAGE
550                CDEF:ds0modified=TIME,begintime,LT,TIME,endtime,GT,+,UNKN,ds0,IF
552        This will return an UNKNOWN if either comparison returns true.
554        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.
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.
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.
564        It would be something like:
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,+,+,+
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.
576        Define an extra CDEF that is unknown if all is okay and is infinite if
577        there is an unknown value:
579            CDEF:wrongdata=allusers,UN,INF,UNKN,IF
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".
586        The logic is: if (allusers == unknown) then return INF else return
587        UNKN.
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.
594           AREA:allusers#0000FF:combined user count
595           AREA:wrongdata#FF0000:unknown data
597        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:
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:
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
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.
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:
626        ·   It improves the readability of the script.
628        ·   It can be used inside GPRINT to display the total number of users.
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:
633           CDEF:wrongdata=users1,users2,users3,users4,+,+,+,UN,INF,UNKN,IF
635        If you do so, you won't be able to use these next GPRINTs:
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"
643 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
644        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
646        To convert Celsius into Fahrenheit use the formula F=9/5*C+32
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"
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:
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
669        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
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
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:
685         idat1,UN --> a
686         0        --> b
687         idat1    --> c
688         if (a) then (b) else (c)
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.
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.
702        I\bIn\bnf\bfi\bin\bni\bit\bty\by d\bde\bem\bmo\bo
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
718        This demo demonstrates two ways to use infinity. It is a bit tricky to
719        see what happens in the "background" CDEF.
721           "val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF"
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.
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.
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.
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".
743        The second CDEF has been discussed earlier in this document so we won't
744        do that here.
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).
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.
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,*"
762        F\bFi\bil\blt\bte\ber\bri\bin\bng\bg d\bda\bat\bta\ba
764        You may do some complex data filtering:
766          MEDIAN FILTER: filters shot noise
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'
776          DERIVATE:
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'
786 O\bOu\but\bt o\bof\bf i\bid\bde\bea\bas\bs f\bfo\bor\br n\bno\bow\bw
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@vandenbogaerdt.nl>
792        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!
794 S\bSE\bEE\bE A\bAL\bLS\bSO\bO
795        The RRDtool manpages
797 A\bAU\bUT\bTH\bHO\bOR\bR
798        Alex van den Bogaerdt <alex@vandenbogaerdt.nl>
802 1.3.99909060808                   2009-02-21                   CDEFTUTORIAL(1)