Code

fde0164e1e02d164a4bbcd8239e60f11727b510f
[pkg-rrdtool.git] / doc / rrd-beginners.html
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2 <html xmlns="http://www.w3.org/1999/xhtml">
3 <head>
4 <title>rrd-beginners</title>
5 <link rev="made" href="mailto:root@localhost" />
6 </head>
8 <body style="background-color: white">
10 <p><a name="__index__"></a></p>
11 <!-- INDEX BEGIN -->
12 <!--
14 <ul>
16         <li><a href="#name">NAME</a></li>
17         <li><a href="#synopsis">SYNOPSIS</a></li>
18         <li><a href="#description">DESCRIPTION</a></li>
19         <ul>
21                 <li><a href="#what_makes_rrdtool_so_special">What makes RRDtool so special?</a></li>
22                 <li><a href="#understanding_by_an_example">Understanding by an example</a></li>
23                 <li><a href="#graphical_magic">Graphical Magic</a></li>
24                 <li><a href="#wrapping_rrdtool_within_shell_perl_script">Wrapping RRDtool within Shell/Perl script</a></li>
25                 <ul>
27                         <li><a href="#shell_script__collects_data__updates_database_">Shell script (collects data, updates database)</a></li>
28                         <li><a href="#perl_script__retrieves_data_from_database_and_generates_graphs_and_statistics_">Perl script (retrieves data from database and generates graphs and statistics)</a></li>
29                 </ul>
31         </ul>
33         <li><a href="#author">AUTHOR</a></li>
34 </ul>
35 -->
36 <!-- INDEX END -->
38 <p>
39 </p>
40 <h1><a name="name">NAME</a></h1>
41 <p>rrd-beginners - RRDtool Beginners' Guide</p>
42 <p>
43 </p>
44 <hr />
45 <h1><a name="synopsis">SYNOPSIS</a></h1>
46 <p>Helping new RRDtool users to understand the basics of RRDtool</p>
47 <p>
48 </p>
49 <hr />
50 <h1><a name="description">DESCRIPTION</a></h1>
51 <p>This manual is an attempt to assist beginners in understanding the concepts
52 of RRDtool. It sheds a light on differences between RRDtool and other
53 databases. With help of an example, it explains the structure of RRDtool
54 database. This is followed by an overview of the ``graph'' feature of RRDtool.
55 At the end, it has sample scripts that illustrate the
56 usage/wrapping of RRDtool within Shell or Perl scripts.</p>
57 <p>
58 </p>
59 <h2><a name="what_makes_rrdtool_so_special">What makes RRDtool so special?</a></h2>
60 <p>RRDtool is GNU licensed software developed by Tobias Oetiker, a system
61 manager at the Swiss Federal Institute of Technology. Though it is a
62 database, there are distinct differences between RRDtool databases and other
63 databases as listed below:</p>
64 <ul>
65 <li></li>
66 RRDtool stores data; that makes it a back-end tool. The RRDtool command set
67 allows the creation of graphs; that makes it a front-end tool as well. Other
68 databases just store data and can not create graphs.
69 <p></p>
70 <li></li>
71 In case of linear databases, new data gets appended at the bottom of
72 the database table. Thus its size keeps on increasing, whereas the size of
73 an RRDtool database is determined at creation time. Imagine an RRDtool
74 database as the perimeter of a circle. Data is added along the
75 perimeter. When new data reaches the starting point, it overwrites
76 existing data. This way, the size of an RRDtool database always
77 remains constant. The name ``Round Robin'' stems from this behavior.
78 <p></p>
79 <li></li>
80 Other databases store the values as supplied. RRDtool can be configured to
81 calculate the rate of change from the previous to the current value and
82 store this information instead.
83 <p></p>
84 <li></li>
85 Other databases get updated when values are supplied. The RRDtool database
86 is structured in such a way that it needs data at predefined time
87 intervals. If it does not get a new value during the interval, it stores an
88 UNKNOWN value for that interval. So, when using the RRDtool database, it is
89 imperative to use scripts that run at regular intervals to ensure a constant
90 data flow to update the RRDtool database.
91 <p></p></ul>
92 <p>RRDtool is designed to store time series of data. With every data
93 update, an associated time stamp is stored. Time is always expressed
94 in seconds passed since epoch (01-01-1970). RRDtool can be installed
95 on Unix as well as Windows. It comes with a command set to carry out
96 various operations on RRD databases. This command set can be accessed
97 from the command line, as well as from Shell or Perl scripts. The
98 scripts act as wrappers for accessing data stored in RRDtool
99 databases.</p>
100 <p>
101 </p>
102 <h2><a name="understanding_by_an_example">Understanding by an example</a></h2>
103 <p>The structure of an RRD database is different than other linear databases.
104 Other databases define tables with columns, and many other parameters. These
105 definitions sometimes are very complex, especially in large databases.
106 RRDtool databases are primarily used for monitoring purposes and
107 hence are very simple in structure. The parameters
108 that need to be defined are variables that hold values and archives of those
109 values. Being time sensitive, a couple of time related parameters are also
110 defined. Because of its structure, the definition of an RRDtool database also
111 includes a provision to specify specific actions to take in the absence of
112 update values. Data Source (DS), heartbeat, Date Source Type (DST), Round
113 Robin Archive (RRA), and Consolidation Function (CF) are some of the
114 terminologies related to RRDtool databases.</p>
115 <p>The structure of a database and the terminology associated with it can be
116 best explained with an example.</p>
117 <pre>
118  rrdtool create target.rrd \
119          --start 1023654125 \
120          --step 300 \
121          DS:mem:GAUGE:600:0:671744 \
122          RRA:AVERAGE:0.5:12:24 \
123          RRA:AVERAGE:0.5:288:31</pre>
124 <p>This example creates a database named <em>target.rrd</em>. Start time
125 (1'023'654'125) is specified in total number of seconds since epoch
126 (time in seconds since 01-01-1970). While updating the database, the
127 update time is also specified.  This update time MUST be large (later)
128 then start time and MUST be in seconds since epoch.</p>
129 <p>The step of 300 seconds indicates that database expects new values every
130 300 seconds. The wrapper script should be scheduled to run every <strong>step</strong>
131 seconds so that it updates the database every <strong>step</strong> seconds.</p>
132 <p>DS (Data Source) is the actual variable which relates to the parameter on
133 the device that is monitored. Its syntax is</p>
134 <pre>
135  DS:variable_name:DST:heartbeat:min:max</pre>
136 <p><strong>DS</strong> is a key word. <code>variable_name</code> is a name under which the parameter is
137 saved in the database. There can be as many DSs in a database as needed. After
138 every step interval, a new value of DS is supplied to update the database.
139 This value is also called Primary Data Point <strong>(PDP)</strong>. In our example
140 mentioned above, a new PDP is generated every 300 seconds.</p>
141 <p>Note, that if you do NOT supply new datapoints exactly every 300 seconds,
142 this is not a problem, RRDtool will interpolate the data accordingly.</p>
143 <p><strong>DST</strong> (Data Source Type) defines the type of the DS. It can be
144 COUNTER, DERIVE, ABSOLUTE, GAUGE. A DS declared as COUNTER will save
145 the rate of change of the value over a step period. This assumes that
146 the value is always increasing (the difference between the current and
147 the previous value is greater than 0). Traffic counters on a router
148 are an ideal candidate for using COUNTER as DST. DERIVE is the same as
149 COUNTER, but it allows negative values as well. If you want to see the
150 rate of <em>change</em> in free diskspace on your server, then you might
151 want to use the DERIVE data type. ABSOLUTE also saves the rate of
152 change, but it assumes that the previous value is set to 0. The
153 difference between the current and the previous value is always equal
154 to the current value. Thus it just stores the current value divided by
155 the step interval (300 seconds in our example). GAUGE does not save
156 the rate of change. It saves the actual value itself. There are no
157 divisions or calculations. Memory consumption in a server is a typical
158 example of gauge. The difference between the different types DSTs can be
159 explained better with the following example:</p>
160 <pre>
161  Values       = 300, 600, 900, 1200
162  Step         = 300 seconds
163  COUNTER DS   =    1,  1,   1,    1
164  DERIVE DS    =    1,  1,   1,    1
165  ABSOLUTE DS  =    1,  2,   3,    4
166  GAUGE DS     = 300, 600, 900, 1200</pre>
167 <p>The next parameter is <strong>heartbeat</strong>. In our example, heartbeat is 600
168 seconds. If the database does not get a new PDP within 300 seconds, it
169 will wait for another 300 seconds (total 600 seconds).  If it doesn't
170 receive any PDP within 600 seconds, it will save an UNKNOWN value into
171 the database. This UNKNOWN value is a special feature of RRDtool - it
172 is much better than to assume a missing value was 0 (zero) or any
173 other number which might also be a valid data value.  For example, the
174 traffic flow counter on a router keeps increasing. Lets say, a value
175 is missed for an interval and 0 is stored instead of UNKNOWN. Now when
176 the next value becomes available, it will calculate the difference
177 between the current value and the previous value (0) which is not
178 correct. So, inserting the value UNKNOWN makes much more sense here.</p>
179 <p>The next two parameters are the minimum and maximum value,
180 respectively. If the variable to be stored has predictable maximum and
181 minimum values, this should be specified here. Any update value
182 falling out of this range will be stored as UNKNOWN.</p>
183 <p>The next line declares a round robin archive (RRA). The syntax for
184 declaring an RRA is</p>
185 <pre>
186  RRA:CF:xff:step:rows</pre>
187 <p>RRA is the keyword to declare RRAs. The consolidation function (CF)
188 can be AVERAGE, MINIMUM, MAXIMUM, and LAST. The concept of the
189 consolidated data point (CDP) comes into the picture here. A CDP is
190 CFed (averaged, maximum/minimum value or last value) from <em>step</em>
191 number of PDPs. This RRA will hold <em>rows</em> CDPs.</p>
192 <p>Lets have a look at the example above. For the first RRA, 12 (steps)
193 PDPs (DS variables) are AVERAGEed (CF) to form one CDP. 24 (rows) of
194 theses CDPs are archived. Each PDP occurs at 300 seconds. 12 PDPs
195 represent 12 times 300 seconds which is 1 hour. It means 1 CDP (which
196 is equal to 12 PDPs) represents data worth 1 hour. 24 such CDPs
197 represent 1 day (1 hour times 24 CDPs). This means, this RRA is an
198 archive for one day. After 24 CDPs, CDP number 25 will replace the 1st
199 CDP. The second RRA saves 31 CDPs; each CPD represents an AVERAGE
200 value for a day (288 PDPs, each covering 300 seconds = 24
201 hours). Therefore this RRA is an archive for one month. A single
202 database can have many RRAs. If there are multiple DSs, each
203 individual RRA will save data for all the DSs in the database. For
204 example, if a database has 3 DSs and daily, weekly, monthly, and
205 yearly RRAs are declared, then each RRA will hold data from all 3 data
206 sources.</p>
207 <p>
208 </p>
209 <h2><a name="graphical_magic">Graphical Magic</a></h2>
210 <p>Another important feature of RRDtool is its ability to create
211 graphs. The ``graph'' command uses the ``fetch'' command internally to
212 retrieve values from the database. With the retrieved values it draws
213 graphs as defined by the parameters supplied on the command line. A
214 single graph can show different DS (Data Sources) from a database. It
215 is also possible to show the values from more than one database in a
216 single graph. Often, it is necessary to perform some math on the
217 values retrieved from the database before plotting them. For example,
218 in SNMP replies, memory consumption values are usually specified in
219 KBytes and traffic flow on interfaces is specified in Bytes. Graphs
220 for these values will be more meaningful if values are represented in
221 MBytes and mbps. The RRDtool graph command allows to define such
222 conversions. Apart from mathematical calculations, it is also possible
223 to perform logical operations such as greater than, less than, and
224 if/then/else. If a database contains more than one RRA archive, then a
225 question may arise - how does RRDtool decide which RRA archive to use
226 for retrieving the values? RRDtool looks at several things when making
227 its choice. First it makes sure that the RRA covers as much of the
228 graphing time frame as possible. Second it looks at the resolution of
229 the RRA compared to the resolution of the graph. It tries to find one
230 which has the same or higher better resolution. With the ``-r'' option
231 you can force RRDtool to assume a different resolution than the one
232 calculated from the pixel width of the graph.</p>
233 <p>Values of different variables can be presented in 5 different shapes
234 in a graph - AREA, LINE1, LINE2, LINE3, and STACK. AREA is represented
235 by a solid colored area with values as the boundary of this
236 area. LINE1/2/3 (increasing width) are just plain lines representing
237 the values. STACK is also an area but it is ``stack''ed on top AREA or
238 LINE1/2/3. Another important thing to note is that variables are
239 plotted in the order they are defined in the graph command. Therefore
240 care must be taken to define STACK only after defining AREA/LINE. It
241 is also possible to put formatted comments within the graph.  Detailed
242 instructions can be found in the graph manual.</p>
243 <p>
244 </p>
245 <h2><a name="wrapping_rrdtool_within_shell_perl_script">Wrapping RRDtool within Shell/Perl script</a></h2>
246 <p>After understanding RRDtool it is now a time to actually use RRDtool
247 in scripts. Tasks involved in network management are data collection,
248 data storage, and data retrieval. In the following example, the
249 previously created target.rrd database is used. Data collection and
250 data storage is done using Shell scripts. Data retrieval and report
251 generation is done using Perl scripts. These scripts are shown below:</p>
252 <p>
253 </p>
254 <h3><a name="shell_script__collects_data__updates_database_">Shell script (collects data, updates database)</a></h3>
255 <pre>
256  #!/bin/sh
257  a=0
258  while [ &quot;$a&quot; == 0 ]; do
259  snmpwalk -c public 192.168.1.250 hrSWRunPerfMem &gt; snmp_reply
260      total_mem=`awk 'BEGIN {tot_mem=0}
261                            { if ($NF == &quot;KBytes&quot;)
262                              {tot_mem=tot_mem+$(NF-1)}
263                            }
264                      END {print tot_mem}' snmp_reply`
265      # I can use N as a replacement for the current time
266      rrdtool update target.rrd N:$total_mem
267      # sleep until the next 300 seconds are full
268      perl -e 'sleep 300 - time % 300'
269  done # end of while loop</pre>
270 <p>
271 </p>
272 <h3><a name="perl_script__retrieves_data_from_database_and_generates_graphs_and_statistics_">Perl script (retrieves data from database and generates graphs and statistics)</a></h3>
273 <pre>
274  #!/usr/bin/perl -w
275  # This script fetches data from target.rrd, creates a graph of memory
276  # consumption on the target (Dual P3 Processor 1 GHz, 656 MB RAM)</pre>
277 <pre>
278  # call the RRD perl module
279  use lib qw( /usr/local/rrdtool-1.0.41/lib/perl ../lib/perl );
280  use RRDs;
281  my $cur_time = time();                # set current time
282  my $end_time = $cur_time - 86400;     # set end time to 24 hours ago
283  my $start_time = $end_time - 2592000; # set start 30 days in the past</pre>
284 <pre>
285  # fetch average values from the RRD database between start and end time
286  my ($start,$step,$ds_names,$data) =
287      RRDs::fetch(&quot;target.rrd&quot;, &quot;AVERAGE&quot;,
288                  &quot;-r&quot;, &quot;600&quot;, &quot;-s&quot;, &quot;$start_time&quot;, &quot;-e&quot;, &quot;$end_time&quot;);
289  # save fetched values in a 2-dimensional array
290  my $rows = 0;
291  my $columns = 0;
292  my $time_variable = $start;
293  foreach $line (@$data) {
294    $vals[$rows][$columns] = $time_variable;
295    $time_variable = $time_variable + $step;
296    foreach $val (@$line) {
297            $vals[$rows][++$columns] = $val;}
298    $rows++;
299    $columns = 0;
300  }
301  my $tot_time = 0;
302  my $count = 0;
303  # save the values from the 2-dimensional into a 1-dimensional array
304  for $i ( 0 .. $#vals ) {
305      $tot_mem[$count] = $vals[$i][1];
306      $count++;
307  }
308  my $tot_mem_sum = 0;
309  # calculate the total of all values
310  for $i ( 0 .. ($count-1) ) {
311      $tot_mem_sum = $tot_mem_sum + $tot_mem[$i];
312  }
313  # calculate the average of the array
314  my $tot_mem_ave = $tot_mem_sum/($count);
315  # create the graph
316  RRDs::graph (&quot;/images/mem_$count.png&quot;,   \
317              &quot;--title= Memory Usage&quot;,    \
318              &quot;--vertical-label=Memory Consumption (MB)&quot;, \
319              &quot;--start=$start_time&quot;,      \
320              &quot;--end=$end_time&quot;,          \
321              &quot;--color=BACK#CCCCCC&quot;,      \
322              &quot;--color=CANVAS#CCFFFF&quot;,    \
323              &quot;--color=SHADEB#9999CC&quot;,    \
324              &quot;--height=125&quot;,             \
325              &quot;--upper-limit=656&quot;,        \
326              &quot;--lower-limit=0&quot;,          \
327              &quot;--rigid&quot;,                  \
328              &quot;--base=1024&quot;,              \
329              &quot;DEF:tot_mem=target.rrd:mem:AVERAGE&quot;, \
330              &quot;CDEF:tot_mem_cor=tot_mem,0,671744,LIMIT,UN,0,tot_mem,IF,1024,/&quot;,\
331              &quot;CDEF:machine_mem=tot_mem,656,+,tot_mem,-&quot;,\
332              &quot;COMMENT:Memory Consumption between $start_time&quot;,\
333              &quot;COMMENT:    and $end_time                     &quot;,\
334              &quot;HRULE:656#000000:Maximum Available Memory - 656 MB&quot;,\
335              &quot;AREA:machine_mem#CCFFFF:Memory Unused&quot;,   \
336              &quot;AREA:tot_mem_cor#6699CC:Total memory consumed in MB&quot;);
337  my $err=RRDs::error;
338  if ($err) {print &quot;problem generating the graph: $err\n&quot;;}
339  # print the output
340  print &quot;Average memory consumption is &quot;;
341  printf &quot;%5.2f&quot;,$tot_mem_ave/1024;
342  print &quot; MB. Graphical representation can be found at /images/mem_$count.png.&quot;;</pre>
343 <p>
344 </p>
345 <hr />
346 <h1><a name="author">AUTHOR</a></h1>
347 <p>Ketan Patel &lt;<a href="mailto:k2pattu@yahoo.com">k2pattu@yahoo.com</a>&gt;</p>
349 </body>
351 </html>