{
    "mode": "man",
    "parameter": "CDEFTUTORIAL",
    "section": "",
    "url": "https://www.chedong.com/phpMan.php/man/CDEFTUTORIAL/json",
    "generated": "2026-10-22T12:37:07Z",
    "sections": {
        "NAME": {
            "content": "cdeftutorial - Alex van den Bogaerdt's CDEF tutorial\n",
            "subsections": []
        },
        "DESCRIPTION": {
            "content": "Intention of this document: to provide some examples of the commonly used parts of RRDtool's\nCDEF language.\n\nIf you think some important feature is not explained properly, and if adding it to this\ndocument would benefit most users, please do ask me to add it.  I will then try to provide an\nanswer in the next release of this tutorial.  No feedback equals no changes! Additions to\nthis document are also welcome.  -- Alex van den Bogaerdt <alex@vandenbogaerdt.nl>\n",
            "subsections": [
                {
                    "name": "Why this tutorial?",
                    "content": "One of the powerful parts of RRDtool is its ability to do all sorts of calculations on the\ndata retrieved from its databases. However, RRDtool's many options and syntax make it\ndifficult for the average user to understand. The manuals are good at explaining what these\noptions do; however they do not (and should not) explain in detail why they are useful. As\nwith my RRDtool tutorial: if you want a simple document in simple language you should read\nthis tutorial.  If you are happy with the official documentation, you may find this document\ntoo simple or even boring. If you do choose to read this tutorial, I also expect you to have\nread and fully understand my other tutorial.\n"
                },
                {
                    "name": "More reading",
                    "content": "If you have difficulties with the way I try to explain it please read Steve Rader's\nrpntutorial. It may help you understand how this all works.\n\nWhat are CDEFs?\nWhen retrieving data from an RRD, you are using a \"DEF\" to work with that data. Think of it\nas a variable that changes over time (where time is the x-axis). The value of this variable\nis what is found in the database at that particular time and you can't do any modifications\non it. This is what CDEFs are for: they takes values from DEFs and perform calculations on\nthem.\n"
                }
            ]
        },
        "Syntax": {
            "content": "DEF:varname1=some.rrd:dsname:CF\nCDEF:varname2=RPNexpression\n\nYou first define \"varname1\" to be data collected from data source \"dsname\" found in RRD\n\"some.rrd\" with consolidation function \"CF\".\n\nAssume the ifInOctets SNMP counter is saved in mrtg.rrd as the DS \"in\".  Then the following\nDEF defines a variable for the average of that data source:\n\nDEF:inbytes=mrtg.rrd:in:AVERAGE\n\nSay you want to display bits per second (instead of bytes per second as stored in the\ndatabase.)  You have to define a calculation (hence \"CDEF\") on variable \"inbytes\" and use\nthat variable (inbits) instead of the original:\n\nCDEF:inbits=inbytes,8,*\n\nThis tells RRDtool to multiply inbytes by eight to get inbits. I'll explain later how this\nworks. In the graphing or printing functions, you can now use inbits where you would use\ninbytes otherwise.\n\nNote that the variable name used in the CDEF (inbits) must not be the same as the variable\nnamed in the DEF (inbytes)!\n\nRPN-expressions\nRPN is short-hand for Reverse Polish Notation. It works as follows.  You put the variables or\nnumbers on a stack. You also put operations (things-to-do) on the stack and this stack is\nthen processed. The result will be placed on the stack. At the end, there should be exactly\none number left: the outcome of the series of operations. If there is not exactly one number\nleft, RRDtool will complain loudly.\n\nAbove multiplication by eight will look like:\n\n1.  Start with an empty stack\n\n2.  Put the content of variable inbytes on the stack\n\n3.  Put the number eight on the stack\n\n4.  Put the operation multiply on the stack\n\n5.  Process the stack\n\n6.  Retrieve the value from the stack and put it in variable inbits\n\nWe  will  now  do an example with real numbers. Suppose the variable inbytes would have value\n10, the stack would be:\n\n1.  ||\n\n2.  |10|\n\n3.  |10|8|\n\n4.  |10|8|*|\n\n5.  |80|\n\n6.  ||\n\nProcessing the stack (step 5) will retrieve one value from the stack (from the right at  step\n4).  This  is  the  operation  multiply and this takes two values off the stack as input. The\nresult is put back on the stack (the value 80 in this case).  For  multiplication  the  order\ndoesn't  matter,  but  for other operations like subtraction and division it does.  Generally\nspeaking you have the following order:\n\ny = A - B  -->  y=minus(A,B)  -->  CDEF:y=A,B,-\n\nThis is not very intuitive (at least most people don't think so). For the function f(A,B) you\nreverse the position of \"f\", but you do not reverse the order of the variables.\n",
            "subsections": []
        },
        "Converting your wishes to RPN": {
            "content": "First, get a clear picture of what you want to do. Break down the problem in smaller portions\nuntil they cannot be split anymore. Then it is rather simple to convert your ideas into RPN.\n\nSuppose you have several RRDs and would like to add up some counters in them. These could be,\nfor instance, the counters for every WAN link you are monitoring.\n\nYou have:\n\nrouter1.rrd with link1in link2in\nrouter2.rrd with link1in link2in\nrouter3.rrd with link1in link2in\n\nSuppose you would like to add up all these counters, except for link2in  inside  router2.rrd.\nYou need to do:\n\n(in this example, \"router1.rrd:link1in\" means the DS link1in inside the RRD router1.rrd)\n\nrouter1.rrd:link1in\nrouter1.rrd:link2in\nrouter2.rrd:link1in\nrouter3.rrd:link1in\nrouter3.rrd:link2in\n--------------------   +\n(outcome of the sum)\n\nAs a mathematical function, this could be written:\n\n\"add(router1.rrd:link1in  , router1.rrd:link2in , router2.rrd:link1in , router3.rrd:link1in ,\nrouter3.rrd:link2.in)\"\n\nWith RRDtool and RPN, first, define the inputs:\n\nDEF:a=router1.rrd:link1in:AVERAGE\nDEF:b=router1.rrd:link2in:AVERAGE\nDEF:c=router2.rrd:link1in:AVERAGE\nDEF:d=router3.rrd:link1in:AVERAGE\nDEF:e=router3.rrd:link2in:AVERAGE\n\nNow, the mathematical function becomes: \"add(a,b,c,d,e)\"\n\nIn RPN, there's no operator that sums more  than  two  values  so  you  need  to  do  several\nadditions. You add a and b, add c to the result, add d to the result and add e to the result.\n\npush a:         a     stack contains the value of a\npush b and add: b,+   stack contains the result of a+b\npush c and add: c,+   stack contains the result of a+b+c\npush d and add: d,+   stack contains the result of a+b+c+d\npush e and add: e,+   stack contains the result of a+b+c+d+e\n\nWhat was calculated here would be written down as:\n\n( ( ( (a+b) + c) + d) + e) >\n\nThis is in RPN:  \"CDEF:result=a,b,+,c,+,d,+,e,+\"\n\nThis is correct but it can be made more clear to humans. It does not matter if you add a to b\nand  then add c to the result or first add b to c and then add a to the result. This makes it\npossible  to  rewrite  the  RPN  into  \"CDEF:result=a,b,c,d,e,+,+,+,+\"  which  is   evaluated\ndifferently:\n\npush value of variable a on the stack: a\npush value of variable b on the stack: a b\npush value of variable c on the stack: a b c\npush value of variable d on the stack: a b c d\npush value of variable e on the stack: a b c d e\npush operator + on the stack:          a b c d e +\nand process it:                        a b c P   (where P == d+e)\npush operator + on the stack:          a b c P +\nand process it:                        a b Q     (where Q == c+P)\npush operator + on the stack:          a b Q +\nand process it:                        a R       (where R == b+Q)\npush operator + on the stack:          a R +\nand process it:                        S         (where S == a+R)\n\nAs  you can see the RPN expression \"a,b,c,d,e,+,+,+,+,+\" will evaluate in \"((((d+e)+c)+b)+a)\"\nand it has the same outcome as \"a,b,+,c,+,d,+,e,+\".  This is called the  commutative  law  of\naddition, but you may forget this right away, as long as you remember what it means.\n\nNow look at an expression that contains a multiplication:\n\nFirst in normal math: \"let result = a+b*c\". In this case you can't choose the order yourself,\nyou have to start with the multiplication and then add a to it. You may alter the position of\nb and c, you must not alter the position of a and b.\n\nYou  have to take this in consideration when converting this expression into RPN. Read it as:\n\"Add the  outcome  of  b*c  to  a\"  and  then  it  is  easy  to  write  the  RPN  expression:\n\"result=a,b,c,*,+\" Another expression that would return the same: \"result=b,c,*,a,+\"\n\nIn  normal  math,  you  may encounter something like \"a*(b+c)\" and this can also be converted\ninto RPN. The parenthesis just tell you to first add b and c, and then multiply  a  with  the\nresult.  Again, now it is easy to write it in RPN: \"result=a,b,c,+,*\". Note that this is very\nsimilar to one of the expressions in the previous paragraph, only the multiplication and  the\naddition changed places.\n\nWhen  you have problems with RPN or when RRDtool is complaining, it's usually a good thing to\nwrite down the stack on a piece of paper and see what happens.  Have  the  manual  ready  and\npretend  to be RRDtool.  Just do all the math by hand to see what happens, I'm sure this will\nsolve most, if not all, problems you encounter.\n",
            "subsections": []
        },
        "Some special numbers": {
            "content": "",
            "subsections": [
                {
                    "name": "The unknown value",
                    "content": "Sometimes collecting your data will fail. This can be very common, especially  when  querying\nover  busy  links. RRDtool can be configured to allow for one (or even more) unknown value(s)\nand calculate the missing update. You can, for instance, query your device every minute. This\nis creating one so called PDP or primary data point per minute. If you defined  your  RRD  to\ncontain  an  RRA  that  stores 5-minute values, you need five of those PDPs to create one CDP\n(consolidated data point).  These PDPs can become unknown in two cases:\n\n1.  The updates are too far apart. This is tuned using the \"heartbeat\" setting.\n\n2.  The update was set to unknown on purpose  by  inserting  no  value  (using  the  template\noption) or by using \"U\" as the value to insert.\n\nWhen  a CDP is calculated, another mechanism determines if this CDP is valid or not. If there\nare too many PDPs unknown, the CDP is unknown as well.  This is determined by the xff factor.\nPlease note that one unknown counter update can result in two unknown PDPs! If you only allow\nfor one unknown PDP per CDP, this makes the CDP go unknown!\n\nSuppose the counter increments with one per second and you retrieve it every minute:\n\ncounter value    resulting rate\n10'000\n10'060            1; (10'060-10'000)/60 == 1\n10'120            1; (10'120-10'060)/60 == 1\nunknown           unknown; you don't know the last value\n10'240            unknown; you don't know the previous value\n10'300            1; (10'300-10'240)/60 == 1\n\nIf the CDP was to be calculated from the last five updates, it would get two unknown PDPs and\nthree known PDPs. If xff would have been set to 0.5 which by  the  way  is  a  commonly  used\nfactor,  the  CDP  would  have a known value of 1. If xff would have been set to 0.2 then the\nresulting CDP would be unknown.\n\nYou have to decide the proper values for heartbeat, number  of  PDPs  per  CDP  and  the  xff\nfactor. As you can see from the previous text they define the behavior of your RRA.\n"
                },
                {
                    "name": "Working with unknown data in your database",
                    "content": "As  you have read in the previous chapter, entries in an RRA can be set to the unknown value.\nIf you do calculations with this type of value, the result has to be unknown too. This  means\nthat  an  expression  such as \"result=a,b,+\" will be unknown if either a or b is unknown.  It\nwould be wrong to just ignore the unknown value and return the value of the other  parameter.\nBy doing so, you would assume \"unknown\" means \"zero\" and this is not true.\n\nThere  has  been  a  case where somebody was collecting data for over a year.  A new piece of\nequipment was installed, a new RRD was created and the scripts were changed to add a  counter\nfrom  the  old  database and a counter from the new database. The result was disappointing, a\nlarge part of the statistics seemed to have vanished mysteriously ...  They of course didn't,\nvalues from the old database (known values) were  added  to  values  from  the  new  database\n(unknown values) and the result was unknown.\n\nIn  this  case, it is fairly reasonable to use a CDEF that alters unknown data into zero. The\ncounters of the device were unknown (after all, it wasn't installed yet!) but you  know  that\nthe  data  rate  through  the  device  had to be zero (because of the same reason: it was not\ninstalled).\n\nThere are some examples below that make this change.\n"
                },
                {
                    "name": "Infinity",
                    "content": "Infinite data is another form of a special number. It cannot be graphed because by definition\nyou would never reach the infinite value. You can think of  positive  and  negative  infinity\ndepending on the position relative to zero.\n\nRRDtool  is  capable  of  representing  (-not- graphing!) infinity by stopping at its current\nmaximum (for positive infinity) or minimum  (for  negative  infinity)  without  knowing  this\nmaximum (minimum).\n\nInfinity  in  RRDtool is mostly used to draw an AREA without knowing its vertical dimensions.\nYou can think of it as drawing an AREA with an infinite height and displaying only  the  part\nthat is visible in the current graph. This is probably a good way to approximate infinity and\nit sure allows for some neat tricks. See below for examples.\n"
                },
                {
                    "name": "Working with unknown data and infinity",
                    "content": "Sometimes  you  would like to discard unknown data and pretend it is zero (or any other value\nfor that matter) and sometimes you would like to pretend  that  known  data  is  unknown  (to\ndiscard  known-to-be-wrong data).  This is why CDEFs have support for unknown data. There are\nalso examples available that show unknown data by using infinity.\n"
                }
            ]
        },
        "Some examples": {
            "content": "",
            "subsections": [
                {
                    "name": "Example: using a recently created RRD",
                    "content": "You are keeping statistics on your router for over a year  now.  Recently  you  installed  an\nextra router and you would like to show the combined throughput for these two devices.\n\nIf  you  just  add  up  the counters from router.rrd and router2.rrd, you will add known data\n(from router.rrd) to unknown data (from router2.rrd) for the bigger part of your  stats.  You\ncould solve this in a few ways:\n\n•   While  creating  the new database, fill it with zeros from the start to now.  You have to\nmake the database start at or before the least recent time in the other database.\n\n•   Alternatively, you could use CDEF and alter unknown data to zero.\n\nBoth methods have their pros and cons. The first method is troublesome and if you want to  do\nthat  you have to figure it out yourself. It is not possible to create a database filled with\nzeros, you have to put them in manually. Implementing the second method is described next:\n\nWhat we want is: \"if the value is unknown, replace it with zero\". This could  be  written  in\npseudo-code  as:   if  (value is unknown) then (zero) else (value). When reading the rrdgraph\nmanual you notice the \"UN\" function that returns zero  or  one.  You  also  notice  the  \"IF\"\nfunction that takes zero or one as input.\n\nFirst look at the \"IF\" function. It takes three values from the stack, the first value is the\ndecision  point, the second value is returned to the stack if the evaluation is \"true\" and if\nnot, the third value is returned to the stack. We want  the  \"UN\"  function  to  decide  what\nhappens so we combine those two functions in one CDEF.\n\nLets write down the two possible paths for the \"IF\" function:\n\nif true  return a\nif false return b\n\nIn RPN:  \"result=x,a,b,IF\" where \"x\" is either true or false.\n\nNow  we have to fill in \"x\", this should be the \"(value is unknown)\" part and this is in RPN:\n\"result=value,UN\"\n\nWe now combine them: \"result=value,UN,a,b,IF\" and when we fill in the appropriate things  for\n\"a\" and \"b\" we're finished:\n\n\"CDEF:result=value,UN,0,value,IF\"\n\nYou  may  want  to  read  Steve  Rader's  RPN  guide  if you have difficulties with the way I\nexplained this last example.\n\nIf you want to check this RPN expression, just mimic RRDtool behavior:\n\nFor any known value, the expression evaluates as follows:\nCDEF:result=value,UN,0,value,IF  (value,UN) is not true so it becomes 0\nCDEF:result=0,0,value,IF         \"IF\" will return the 3rd value\nCDEF:result=value                The known value is returned\n\nFor the unknown value, this happens:\nCDEF:result=value,UN,0,value,IF  (value,UN) is true so it becomes 1\nCDEF:result=1,0,value,IF         \"IF\" sees 1 and returns the 2nd value\nCDEF:result=0                    Zero is returned\n\nOf course, if you would like to see another value instead of zero, you  can  use  that  other\nvalue.\n\nEventually,  when  all unknown data is removed from the RRD, you may want to remove this rule\nso that unknown data is properly displayed.\n"
                },
                {
                    "name": "Example: better handling of unknown data, by using time",
                    "content": "The above example has one drawback. If you  do  log  unknown  data  in  your  database  after\ninstalling  your  new equipment, it will also be translated into zero and therefore you won't\nsee that there was a problem. This is not good and what you really want to do is:\n\n•   If there is unknown data, look at the time that this sample was taken.\n\n•   If the unknown value is before time xxx, make it zero.\n\n•   If it is after time xxx, leave it as unknown data.\n\nThis is doable: you can compare the time that the  sample  was  taken  to  some  known  time.\nAssuming  you  started to monitor your device on Friday September 17, 1999, 00:35:57 MET DST.\nTranslate this time in seconds since 1970-01-01 and it becomes 937'521'357.  If  you  process\nunknown values that were received after this time, you want to leave them unknown and if they\nwere  \"received\"  before  this  time,  you  want  to  translate  them  into  zero (so you can\neffectively ignore them while adding them to your other routers counters).\n\nTranslating Friday September 17, 1999, 00:35:57 MET DST into 937'521'357 can be done by,  for\ninstance, using gnu date:\n\ndate -d \"19990917 00:35:57\" +%s\n\nYou could also dump the database and see where the data starts to be known. There are several\nother ways of doing this, just pick one.\n\nNow  we have to create the magic that allows us to process unknown values different depending\non the time that the sample was taken.  This is a three step process:\n\n1.  If the timestamp of the value is after 937'521'357, leave it as is.\n\n2.  If the value is a known value, leave it as is.\n\n3.  Change the unknown value into zero.\n\nLets look at part one:\n\nif (true) return the original value\n\nWe rewrite this:\n\nif (true) return \"a\"\nif (false) return \"b\"\n\nWe need to calculate true or false from step 1. There is a function  available  that  returns\nthe  timestamp  for the current sample. It is called, how surprisingly, \"TIME\". This time has\nto be compared to a constant number, we need \"GT\". The output of \"GT\" is true  or  false  and\nthis is good input to \"IF\". We want \"if (time > 937521357) then (return a) else (return b)\".\n\nThis process was already described thoroughly in the previous chapter so lets do it quick:\n\nif (x) then a else b\nwhere x represents \"time>937521357\"\nwhere a represents the original value\nwhere b represents the outcome of the previous example\n\ntime>937521357       --> TIME,937521357,GT\n\nif (x) then a else b --> x,a,b,IF\nsubstitute x         --> TIME,937521357,GT,a,b,IF\nsubstitute a         --> TIME,937521357,GT,value,b,IF\nsubstitute b         --> TIME,937521357,GT,value,value,UN,0,value,IF,IF\n\nWe end up with: \"CDEF:result=TIME,937521357,GT,value,value,UN,0,value,IF,IF\"\n\nThis looks very complex, however, as you can see, it was not too hard to come up with.\n"
                },
                {
                    "name": "Example: Pretending weird data isn't there",
                    "content": "Suppose you have a problem that shows up as huge spikes in your graph.  You know this happens\nand why, so you decide to work around the problem.  Perhaps you're using your network to do a\nbackup at night and by doing so you get almost 10mb/s while the rest of your network activity\ndoes not produce numbers higher than 100kb/s.\n\nThere are two options:\n\n1.  If  the number exceeds 100kb/s it is wrong and you want it masked out by changing it into\nunknown.\n\n2.  You don't want the graph to show more than 100kb/s.\n\nPseudo code: if (number > 100) then unknown else number or Pseudo code:  if  (number  >  100)\nthen 100 else number.\n\nThe  second  \"problem\" may also be solved by using the rigid option of RRDtool graph, however\nthis has not the same result. In this  example  you  can  end  up  with  a  graph  that  does\nautoscaling. Also, if you use the numbers to display maxima they will be set to 100kb/s.\n\nWe   use   \"IF\"   and   \"GT\"   again.  \"if  (x)  then  (y)  else  (z)\"  is  written  down  as\n\"CDEF:result=x,y,z,IF\"; now fill in x, y and z.  For x  you  fill  in  \"number  greater  than\n100kb/s\"  becoming  \"number,100000,GT\"  (kilo is 1'000 and b/s is what we measure!).  The \"z\"\npart is \"number\" in both cases and the \"y\" part is either \"UNKN\" for unknown or \"100000\"  for\n100kb/s.\n\nThe two CDEF expressions would be:\n\nCDEF:result=number,100000,GT,UNKN,number,IF\nCDEF:result=number,100000,GT,100000,number,IF\n"
                },
                {
                    "name": "Example: working on a certain time span",
                    "content": "If you want a graph that spans a few weeks, but would only want to see some routers' data for\none  week,  you  need  to  \"hide\" the rest of the time frame. Don't ask me when this would be\nuseful, it's just here for the example :)\n\nWe need to compare the time stamp  to  a  begin  date  and  an  end  date.   Comparing  isn't\ndifficult:\n\nTIME,begintime,GE\nTIME,endtime,LE\n\nThese  two  parts  of the CDEF produce either 0 for false or 1 for true.  We can now check if\nthey are both 0 (or 1) using a few IF statements but, as Wataru Satoh pointed out, we can use\nthe \"*\" or \"+\" functions as logical AND and logical OR.\n\nFor \"*\", the result will be zero (false) if either one of the two  operators  is  zero.   For\n\"+\",  the result will only be false (0) when two false (0) operators will be added.  Warning:\n*any* number not equal to 0 will  be  considered  \"true\".  This  means  that,  for  instance,\n\"-1,1,+\" (which should be \"true or true\") will become FALSE ...  In other words, use \"+\" only\nif you know for sure that you have positive numbers (or zero) only.\n\nLet's compile the complete CDEF:\n\nDEF:ds0=router1.rrd:AVERAGE\nCDEF:ds0modified=TIME,begintime,GT,TIME,endtime,LE,*,ds0,UNKN,IF\n\nThis  will  return the value of ds0 if both comparisons return true. You could also do it the\nother way around:\n\nDEF:ds0=router1.rrd:AVERAGE\nCDEF:ds0modified=TIME,begintime,LT,TIME,endtime,GT,+,UNKN,ds0,IF\n\nThis will return an UNKNOWN if either comparison returns true.\n"
                },
                {
                    "name": "Example: You suspect to have problems and want to see unknown data.",
                    "content": "Suppose you add up the number of active users on several terminal servers.  If  one  of  them\ndoesn't  give  an answer (or an incorrect one) you get \"NaN\" in the database (\"Not a Number\")\nand NaN is evaluated as Unknown.\n\nIn this case, you would like to be alerted to it and the sum of the remaining values is of no\nvalue to you.\n\nIt would be something like:\n\nDEF:users1=location1.rrd:onlineTS1:LAST\nDEF:users2=location1.rrd:onlineTS2:LAST\nDEF:users3=location2.rrd:onlineTS1:LAST\nDEF:users4=location2.rrd:onlineTS2:LAST\nCDEF:allusers=users1,users2,users3,users4,+,+,+\n\nIf you now plot allusers, unknown data in one of users1..users4 will show up as a gap in your\ngraph. You want to modify this to show a bright red line, not a gap.\n\nDefine an extra CDEF that is unknown if all is okay and is infinite if there  is  an  unknown\nvalue:\n\nCDEF:wrongdata=allusers,UN,INF,UNKN,IF\n\n\"allusers,UN\"  will evaluate to either true or false, it is the (x) part of the \"IF\" function\nand it checks if allusers is unknown.  The (y) part of the \"IF\"  function  is  set  to  \"INF\"\n(which means infinity) and the (z) part of the function returns \"UNKN\".\n\nThe logic is: if (allusers == unknown) then return INF else return UNKN.\n\nYou  can  now  use  AREA to display this \"wrongdata\" in bright red. If it is unknown (because\nallusers is known) then the red AREA won't show up.  If the value is INF (because allusers is\nunknown) then the red AREA will be filled in on the graph at that particular time.\n\nAREA:allusers#0000FF:combined user count\nAREA:wrongdata#FF0000:unknown data\n"
                },
                {
                    "name": "Same example useful with STACKed data:",
                    "content": "If you use stack in the previous example (as I would do) then you don't add  up  the  values.\nTherefore,  there is no relationship between the four values and you don't get a single value\nto test.  Suppose users3 would be unknown at one point in time: users1 is plotted, users2  is\nstacked  on top of users1, users3 is unknown and therefore nothing happens, users4 is stacked\non top of users2.  Add the extra CDEFs anyway and use them to overlay the \"normal\" graph:\n\nDEF:users1=location1.rrd:onlineTS1:LAST\nDEF:users2=location1.rrd:onlineTS2:LAST\nDEF:users3=location2.rrd:onlineTS1:LAST\nDEF:users4=location2.rrd:onlineTS2:LAST\nCDEF:allusers=users1,users2,users3,users4,+,+,+\nCDEF:wrongdata=allusers,UN,INF,UNKN,IF\nAREA:users1#0000FF:users at ts1\nSTACK:users2#00FF00:users at ts2\nSTACK:users3#00FFFF:users at ts3\nSTACK:users4#FFFF00:users at ts4\nAREA:wrongdata#FF0000:unknown data\n\nIf there is unknown data in one of users1..users4, the \"wrongdata\" AREA  will  be  drawn  and\nbecause  it  starts  at  the X-axis and has infinite height it will effectively overwrite the\nSTACKed parts.\n\nYou could combine the two CDEF lines into one (we don't use \"allusers\")  if  you  like.   But\nthere are good reasons for writing two CDEFS:\n\n•   It improves the readability of the script.\n\n•   It can be used inside GPRINT to display the total number of users.\n\nIf  you choose to combine them, you can substitute the \"allusers\" in the second CDEF with the\npart after the equal sign from the first line:\n\nCDEF:wrongdata=users1,users2,users3,users4,+,+,+,UN,INF,UNKN,IF\n\nIf you do so, you won't be able to use these next GPRINTs:\n\nCOMMENT:\"Total number of users seen\"\nGPRINT:allusers:MAX:\"Maximum: %6.0lf\"\nGPRINT:allusers:MIN:\"Minimum: %6.0lf\"\nGPRINT:allusers:AVERAGE:\"Average: %6.0lf\"\nGPRINT:allusers:LAST:\"Current: %6.0lf\\n\"\n"
                }
            ]
        },
        "The examples from the RRD graph manual page": {
            "content": "",
            "subsections": [
                {
                    "name": "Degrees Celsius vs. Degrees Fahrenheit",
                    "content": "To convert Celsius into Fahrenheit use the formula F=9/5*C+32\n\nrrdtool graph demo.png --title=\"Demo Graph\" \\\nDEF:cel=demo.rrd:exhaust:AVERAGE \\\nCDEF:far=9,5,/,cel,*,32,+ \\\nLINE2:cel#00a000:\"D. Celsius\" \\\nLINE2:far#ff0000:\"D. Fahrenheit\\c\"\n\nThis example gets the DS called \"exhaust\" from database \"demo.rrd\" and  puts  the  values  in\nvariable \"cel\". The CDEF used is evaluated as follows:\n\nCDEF:far=9,5,/,cel,*,32,+\n1. push 9, push 5\n2. push function \"divide\" and process it\nthe stack now contains 9/5\n3. push variable \"cel\"\n4. push function \"multiply\" and process it\nthe stack now contains 9/5*cel\n5. push 32\n6. push function \"plus\" and process it\nthe stack contains now the temperature in Fahrenheit\n"
                },
                {
                    "name": "Changing unknown into zero",
                    "content": "rrdtool graph demo.png --title=\"Demo Graph\" \\\nDEF:idat1=interface1.rrd:ds0:AVERAGE \\\nDEF:idat2=interface2.rrd:ds0:AVERAGE \\\nDEF:odat1=interface1.rrd:ds1:AVERAGE \\\nDEF:odat2=interface2.rrd:ds1:AVERAGE \\\nCDEF:agginput=idat1,UN,0,idat1,IF,idat2,UN,0,idat2,IF,+,8,* \\\nCDEF:aggoutput=odat1,UN,0,odat1,IF,odat2,UN,0,odat2,IF,+,8,* \\\nAREA:agginput#00cc00:Input Aggregate \\\nLINE1:aggoutput#0000FF:Output Aggregate\n\nThese  two  CDEFs  are built from several functions. It helps to split them when viewing what\nthey do. Starting with the first CDEF we would get:\n\nidat1,UN --> a\n0        --> b\nidat1    --> c\nif (a) then (b) else (c)\n\nThe result is therefore \"0\" if it is true that \"idat1\" equals \"UN\".   If  not,  the  original\nvalue  of  \"idat1\"  is  put  back  on  the  stack.  Lets call this answer \"d\". The process is\nrepeated for the next five items on the stack, it is done the same  and  will  return  answer\n\"h\".  The  resulting  stack  is  therefore  \"d,h\".   The  expression  has  been simplified to\n\"d,h,+,8,*\" and it will now be easy to see that we add \"d\" and \"h\", and multiply  the  result\nwith eight.\n\nThe  end  result  is that we have added \"idat1\" and \"idat2\" and in the process we effectively\nignored unknown values. The result is multiplied by eight, most likely to convert bytes/s  to\nbits/s.\n"
                },
                {
                    "name": "Infinity demo",
                    "content": "rrdtool graph example.png --title=\"INF demo\" \\\nDEF:val1=some.rrd:ds0:AVERAGE \\\nDEF:val2=some.rrd:ds1:AVERAGE \\\nDEF:val3=some.rrd:ds2:AVERAGE \\\nDEF:val4=other.rrd:ds0:AVERAGE \\\nCDEF:background=val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF \\\nCDEF:wipeout=val1,val2,val3,val4,+,+,+,UN,INF,UNKN,IF \\\nAREA:background#F0F0F0 \\\nAREA:val1#0000FF:Value1 \\\nSTACK:val2#00C000:Value2 \\\nSTACK:val3#FFFF00:Value3 \\\nSTACK:val4#FFC000:Value4 \\\nAREA:whipeout#FF0000:Unknown\n\nThis  demo  demonstrates  two ways to use infinity. It is a bit tricky to see what happens in\nthe \"background\" CDEF.\n\n\"val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF\"\n\nThis RPN takes the value of \"val4\" as input and then immediately removes it  from  the  stack\nusing  \"POP\".  The  stack  is  now  empty but as a side effect we now know the time that this\nsample was taken.  This time is put on the stack by the \"TIME\" function.\n\n\"TIME,7200,%\" takes the modulo of time and 7'200 (which is two hours).  The  resulting  value\non the stack will be a number in the range from 0 to 7199.\n\nFor people who don't know the modulo function: it is the remainder after an integer division.\nIf  you  divide  16  by  3,  the answer would be 5 and the remainder would be 1. So, \"16,3,%\"\nreturns 1.\n\nWe have the result of \"TIME,7200,%\" on the stack, lets call this \"a\". The start  of  the  RPN\nhas  become  \"a,3600,LE\" and this checks if \"a\" is less or equal than \"3600\". It is true half\nof the time.  We now have to process the rest of the RPN and  this  is  only  a  simple  \"IF\"\nfunction  that  returns  either  \"INF\"  or  \"UNKN\" depending on the time. This is returned to\nvariable \"background\".\n\nThe second CDEF has been discussed earlier in this document so we won't do that here.\n\nNow you can draw the different layers. Start with  the  background  that  is  either  unknown\n(nothing to see) or infinite (the whole positive part of the graph gets filled).\n\nNext you draw the data on top of this background, it will overlay the background. Suppose one\nof  val1..val4  would be unknown, in that case you end up with only three bars stacked on top\nof each other.  You don't want to see this because the data  is  only  valid  when  all  four\nvariables  are  valid.  This is why you use the second CDEF, it will overlay the data with an\nAREA so the data cannot be seen anymore.\n\nIf your data can also have negative values you also need to overwrite the other half of  your\ngraph.  This  can be done in a relatively simple way: what you need is the \"wipeout\" variable\nand place a negative sign before it:  \"CDEF:wipeout2=wipeout,-1,*\"\n"
                },
                {
                    "name": "Filtering data",
                    "content": "You may do some complex data filtering:\n\nMEDIAN FILTER: filters shot noise\n\nDEF:var=database.rrd:traffic:AVERAGE\nCDEF:prev1=PREV(var)\nCDEF:prev2=PREV(prev1)\nCDEF:median=var,prev1,prev2,3,SORT,POP,EXC,POP\nLINE3:median#000077:filtered\nLINE1:prev2#007700:'raw data'\n\n\nDERIVATE:\n\nDEF:var=database.rrd:traffic:AVERAGE\nCDEF:prev1=PREV(var)\nCDEF:time=var,POP,TIME\nCDEF:prevtime=PREV(time)\nCDEF:derivate=var,prev1,-,time,prevtime,-,/\nLINE3:derivate#000077:derivate\nLINE1:var#007700:'raw data'\n"
                }
            ]
        },
        "Out of ideas for now": {
            "content": "This document was created from questions asked by either myself or by  other  people  on  the\nRRDtool  mailing  list.  Please  let  me know if you find errors in it or if you have trouble\nunderstanding it. If you think there should be an addition, mail me: <alex@vandenbogaerdt.nl>\n\nRemember: No feedback equals no changes!\n",
            "subsections": []
        },
        "SEE ALSO": {
            "content": "The RRDtool manpages\n",
            "subsections": []
        },
        "AUTHOR": {
            "content": "Alex van den Bogaerdt <alex@vandenbogaerdt.nl>\n\n1.7.2                                        2024-03-31                              CDEFTUTORIAL(1)",
            "subsections": []
        }
    },
    "summary": "cdeftutorial - Alex van den Bogaerdt's CDEF tutorial",
    "flags": [],
    "examples": [],
    "see_also": []
}