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