mirror of
https://github.com/ntop/ntopng.git
synced 2026-07-28 00:58:40 +00:00
1373 lines
58 KiB
Groff
1373 lines
58 KiB
Groff
.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16)
|
|
.\"
|
|
.\" Standard preamble:
|
|
.\" ========================================================================
|
|
.de Sp \" Vertical space (when we can't use .PP)
|
|
.if t .sp .5v
|
|
.if n .sp
|
|
..
|
|
.de Vb \" Begin verbatim text
|
|
.ft CW
|
|
.nf
|
|
.ne \\$1
|
|
..
|
|
.de Ve \" End verbatim text
|
|
.ft R
|
|
.fi
|
|
..
|
|
.\" Set up some character translations and predefined strings. \*(-- will
|
|
.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
|
|
.\" double quote, and \*(R" will give a right double quote. \*(C+ will
|
|
.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
|
|
.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
|
|
.\" nothing in troff, for use with C<>.
|
|
.tr \(*W-
|
|
.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
|
|
.ie n \{\
|
|
. ds -- \(*W-
|
|
. ds PI pi
|
|
. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
|
|
. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
|
|
. ds L" ""
|
|
. ds R" ""
|
|
. ds C` ""
|
|
. ds C' ""
|
|
'br\}
|
|
.el\{\
|
|
. ds -- \|\(em\|
|
|
. ds PI \(*p
|
|
. ds L" ``
|
|
. ds R" ''
|
|
'br\}
|
|
.\"
|
|
.\" Escape single quotes in literal strings from groff's Unicode transform.
|
|
.ie \n(.g .ds Aq \(aq
|
|
.el .ds Aq '
|
|
.\"
|
|
.\" If the F register is turned on, we'll generate index entries on stderr for
|
|
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
|
|
.\" entries marked with X<> in POD. Of course, you'll have to process the
|
|
.\" output yourself in some meaningful fashion.
|
|
.ie \nF \{\
|
|
. de IX
|
|
. tm Index:\\$1\t\\n%\t"\\$2"
|
|
..
|
|
. nr % 0
|
|
. rr F
|
|
.\}
|
|
.el \{\
|
|
. de IX
|
|
..
|
|
.\}
|
|
.\"
|
|
.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
|
|
.\" Fear. Run. Save yourself. No user-serviceable parts.
|
|
. \" fudge factors for nroff and troff
|
|
.if n \{\
|
|
. ds #H 0
|
|
. ds #V .8m
|
|
. ds #F .3m
|
|
. ds #[ \f1
|
|
. ds #] \fP
|
|
.\}
|
|
.if t \{\
|
|
. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
|
|
. ds #V .6m
|
|
. ds #F 0
|
|
. ds #[ \&
|
|
. ds #] \&
|
|
.\}
|
|
. \" simple accents for nroff and troff
|
|
.if n \{\
|
|
. ds ' \&
|
|
. ds ` \&
|
|
. ds ^ \&
|
|
. ds , \&
|
|
. ds ~ ~
|
|
. ds /
|
|
.\}
|
|
.if t \{\
|
|
. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
|
|
. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
|
|
. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
|
|
. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
|
|
. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
|
|
. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
|
|
.\}
|
|
. \" troff and (daisy-wheel) nroff accents
|
|
.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
|
|
.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
|
|
.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
|
|
.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
|
|
.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
|
|
.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
|
|
.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
|
|
.ds ae a\h'-(\w'a'u*4/10)'e
|
|
.ds Ae A\h'-(\w'A'u*4/10)'E
|
|
. \" corrections for vroff
|
|
.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
|
|
.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
|
|
. \" for low resolution devices (crt and lpr)
|
|
.if \n(.H>23 .if \n(.V>19 \
|
|
\{\
|
|
. ds : e
|
|
. ds 8 ss
|
|
. ds o a
|
|
. ds d- d\h'-1'\(ga
|
|
. ds D- D\h'-1'\(hy
|
|
. ds th \o'bp'
|
|
. ds Th \o'LP'
|
|
. ds ae ae
|
|
. ds Ae AE
|
|
.\}
|
|
.rm #[ #] #H #V #F C
|
|
.\" ========================================================================
|
|
.\"
|
|
.IX Title "RRDTUTORIAL 1"
|
|
.TH RRDTUTORIAL 1 "2013-05-23" "1.4.8" "rrdtool"
|
|
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
|
|
.\" way too many mistakes in technical documents.
|
|
.if n .ad l
|
|
.nh
|
|
.SH "NAME"
|
|
rrdtutorial \- Alex van den Bogaerdt's RRDtool tutorial
|
|
.SH "DESCRIPTION"
|
|
.IX Header "DESCRIPTION"
|
|
RRDtool is written by Tobias Oetiker <tobi@oetiker.ch> with
|
|
contributions from many people all around the world. This document is
|
|
written by Alex van den Bogaerdt <alex@vandenbogaerdt.nl> to help you
|
|
understand what RRDtool is and what it can do for you.
|
|
.PP
|
|
The documentation provided with RRDtool can be too technical for some
|
|
people. This tutorial is here to help you understand the basics of
|
|
RRDtool. It should prepare you to read the documentation yourself.
|
|
It also explains the general things about statistics with a focus on
|
|
networking.
|
|
.SH "TUTORIAL"
|
|
.IX Header "TUTORIAL"
|
|
.SS "Important"
|
|
.IX Subsection "Important"
|
|
Please don't skip ahead in this document! The first part of this
|
|
document explains the basics and may be boring. But if you don't
|
|
understand the basics, the examples will not be as meaningful to you.
|
|
.PP
|
|
Sometimes things change. This example used to provide numbers like
|
|
\&\*(L"0.04\*(R" in stead of \*(L"4.00000e\-02\*(R". Those are really the same numbers,
|
|
just written down differently. Don't be alarmed if a future version
|
|
of rrdtool displays a slightly different form of output. The examples
|
|
in this document are correct for version 1.2.0 of RRDtool.
|
|
.PP
|
|
Also, sometimes bugs do occur. They may also influence the outcome of
|
|
the examples. Example speed4.png was suffering from this (the handling
|
|
of unknown data in an if-statement was wrong). Normal data will be
|
|
just fine (a bug in rrdtool wouldn't last long) but special cases like
|
|
NaN, \s-1INF\s0 and so on may last a bit longer. Try another version if you
|
|
can, or just live with it.
|
|
.PP
|
|
I fixed the speed4.png example (and added a note). There may be other
|
|
examples which suffer from the same or a similar bug. Try to fix it
|
|
yourself, which is a great excercise. But please do not submit your
|
|
result as a fix to the source of this document. Discuss it on the
|
|
user's list, or write to me.
|
|
.SS "What is RRDtool?"
|
|
.IX Subsection "What is RRDtool?"
|
|
RRDtool refers to Round Robin Database tool.
|
|
Round robin is a technique that works with a fixed amount of data, and a
|
|
pointer to the current element. Think of a circle with some dots plotted
|
|
on the edge. These dots are the places where data can be stored. Draw an
|
|
arrow from the center of the circle to one of the dots; this is the pointer.
|
|
When the current data is read or written, the pointer moves to the next
|
|
element. As we are on a circle there is neither a beginning nor an end, you can
|
|
go on and on and on. After a while, all the available places will be used and
|
|
the process automatically reuses old locations. This way, the dataset
|
|
will not grow in size and therefore requires no maintenance.
|
|
RRDtool works with Round Robin Databases (RRDs). It stores and retrieves
|
|
data from them.
|
|
.SS "What data can be put into an \s-1RRD\s0?"
|
|
.IX Subsection "What data can be put into an RRD?"
|
|
You name it, it will probably fit as long as it is some sort of
|
|
time-series data. This means you have to be able to measure some value
|
|
at several points in time and provide this information to RRDtool. If
|
|
you can do this, RRDtool will be able to store it. The values must be
|
|
numerical but don't have to be integers, as is the case with \s-1MRTG\s0 (the
|
|
next section will give more details on this more specialized application).
|
|
.PP
|
|
Many examples below talk about \s-1SNMP\s0 which is an acronym for Simple Network
|
|
Management Protocol. \*(L"Simple\*(R" refers to the protocol. It does not
|
|
mean it is simple to manage or monitor a network. After working your
|
|
way through this document, you should know enough to be able to
|
|
understand what people are talking about. For now, just realize that
|
|
\&\s-1SNMP\s0 can be used to query devices for the values of counters they keep. It
|
|
is the value from those counters that we want to store in the \s-1RRD\s0.
|
|
.SS "What can I do with this tool?"
|
|
.IX Subsection "What can I do with this tool?"
|
|
RRDtool originated from \s-1MRTG\s0 (Multi Router Traffic Grapher). \s-1MRTG\s0
|
|
started as a tiny little script for graphing the use of a university's
|
|
connection to the Internet. \s-1MRTG\s0 was later (ab\-)used as a tool for
|
|
graphing other data sources including temperature, speed, voltage,
|
|
number of printouts and the like.
|
|
.PP
|
|
Most likely you will start to use RRDtool to store and process data
|
|
collected via \s-1SNMP\s0. The data will most likely be bytes (or bits)
|
|
transferred from and to a network or a computer. But it can also be
|
|
used to display tidal waves, solar radiation, power consumption,
|
|
number of visitors at an exhibition, noise levels near an airport,
|
|
temperature on your favorite holiday location, temperature in the
|
|
fridge and whatever your imagination can come up with.
|
|
.PP
|
|
You only need a sensor to measure the data and be able to feed the
|
|
numbers into RRDtool. RRDtool then lets you create a database, store
|
|
data in it, retrieve that data and create graphs in \s-1PNG\s0 format for
|
|
display on a web browser. Those \s-1PNG\s0 images are dependent on the data
|
|
you collected and could be, for instance, an overview of the average
|
|
network usage, or the peaks that occurred.
|
|
.SS "What if I still have problems after reading this document?"
|
|
.IX Subsection "What if I still have problems after reading this document?"
|
|
First of all: read it again! You may have missed something.
|
|
If you are unable to compile the sources and you have a fairly common
|
|
\&\s-1OS\s0, it will probably not be the fault of RRDtool. There may be pre-compiled
|
|
versions around on the Internet. If they come from trusted sources, get
|
|
one of those.
|
|
.PP
|
|
If on the other hand the program works but does not give you the
|
|
expected results, it will be a problem with configuring it. Review
|
|
your configuration and compare it with the examples that follow.
|
|
.PP
|
|
There is a mailing list and an archive of it. Read the list for a few
|
|
weeks and search the archive. It is considered rude to just ask
|
|
a question without searching the archives: your problem may already have been
|
|
solved for somebody else! This is true for most, if not all, mailing lists
|
|
and not only for this particular one. Look in the documentation that
|
|
came with RRDtool for the location and usage of the list.
|
|
.PP
|
|
I suggest you take a moment to subscribe to the mailing list right now
|
|
by sending an email to <rrd\-users\-request@lists.oetiker.ch> with a
|
|
subject of \*(L"subscribe\*(R". If you ever want to leave this list, just write
|
|
an email to the same address but now with a subject of \*(L"unsubscribe\*(R".
|
|
.SS "How will you help me?"
|
|
.IX Subsection "How will you help me?"
|
|
By giving you some detailed descriptions with detailed examples.
|
|
I assume that following the instructions in the order presented
|
|
will give you enough knowledge of RRDtool to experiment for yourself.
|
|
If it doesn't work the first time, don't give up. Reread the stuff that
|
|
you did understand, you may have missed something.
|
|
.PP
|
|
By following the examples you get some hands-on experience and, even
|
|
more important, some background information of how it works.
|
|
.PP
|
|
You will need to know something about hexadecimal numbers. If you don't
|
|
then start with reading bin_dec_hex before you continue here.
|
|
.SS "Your first Round Robin Database"
|
|
.IX Subsection "Your first Round Robin Database"
|
|
In my opinion the best way to learn something is to actually do it.
|
|
Why not start right now? We will create a database, put some values
|
|
in it and extract this data again. Your output should be the same
|
|
as the output that is included in this document.
|
|
.PP
|
|
We will start with some easy stuff and compare a car with a router,
|
|
or compare kilometers (miles if you wish) with bits and bytes. It's
|
|
all the same: some number over some time.
|
|
.PP
|
|
Assume we have a device that transfers bytes to and from the Internet.
|
|
This device keeps a counter that starts at zero when it is turned on,
|
|
increasing with every byte that is transferred. This counter will probably have
|
|
a maximum value. If this value is reached and an extra byte is counted,
|
|
the counter starts over at zero. This is the same as many counters
|
|
in the world such as the mileage counter in a car.
|
|
.PP
|
|
Most discussions about networking talk about bits per second so lets
|
|
get used to that right away. Assume a byte is eight bits and start to
|
|
think in bits not bytes. The counter, however, still counts bytes!
|
|
In the \s-1SNMP\s0 world most of the counters are 32 bits. That means they are
|
|
counting from 0 to 4294967295. We will use these values in the examples.
|
|
The device, when asked, returns the current value of the counter. We
|
|
know the time that has passes since we last asked so we now know how
|
|
many bytes have been transferred ***on average*** per second. This is
|
|
not very hard to calculate. First in words, then in calculations:
|
|
.IP "1." 3
|
|
Take the current counter, subtract the previous value from it.
|
|
.IP "2." 3
|
|
Do the same with the current time and the previous time (in seconds).
|
|
.IP "3." 3
|
|
Divide the outcome of (1) by the outcome of (2), the result is
|
|
the amount of bytes per second. Multiply by eight to get the
|
|
number of bits per second (bps).
|
|
.PP
|
|
.Vb 1
|
|
\& bps = (counter_now \- counter_before) / (time_now \- time_before) * 8
|
|
.Ve
|
|
.PP
|
|
For some people it may help to translate this to an automobile example.
|
|
Do not try this example, and if you do, don't blame me for the results!
|
|
.PP
|
|
People who are not used to think in kilometers per hour can translate
|
|
most into miles per hour by dividing km by 1.6 (close enough).
|
|
I will use the following abbreviations:
|
|
.PP
|
|
.Vb 6
|
|
\& m: meter
|
|
\& km: kilometer (= 1000 meters).
|
|
\& h: hour
|
|
\& s: second
|
|
\& km/h: kilometers per hour
|
|
\& m/s: meters per second
|
|
.Ve
|
|
.PP
|
|
You are driving a car. At 12:05 you read the counter in the dashboard
|
|
and it tells you that the car has moved 12345 km until that moment.
|
|
At 12:10 you look again, it reads 12357 km. This means you have
|
|
traveled 12 km in five minutes. A scientist would translate that
|
|
into meters per second and this makes a nice comparison toward the
|
|
problem of (bytes per five minutes) versus (bits per second).
|
|
.PP
|
|
We traveled 12 kilometers which is 12000 meters. We did that in five
|
|
minutes or 300 seconds. Our speed is 12000m / 300s or 40 m/s.
|
|
.PP
|
|
We could also calculate the speed in km/h: 12 times 5 minutes
|
|
is an hour, so we have to multiply 12 km by 12 to get 144 km/h.
|
|
For our native English speaking friends: that's 90 mph so don't
|
|
try this example at home or where I live :)
|
|
.PP
|
|
Remember: these numbers are averages only. There is no way to figure out
|
|
from the numbers, if you drove at a constant speed. There is an example
|
|
later on in this tutorial that explains this.
|
|
.PP
|
|
I hope you understand that there is no difference in calculating m/s or
|
|
bps; only the way we collect the data is different. Even the k from kilo
|
|
is the same as in networking terms k also means 1000.
|
|
.PP
|
|
We will now create a database where we can keep all these interesting
|
|
numbers. The method used to start the program may differ slightly from
|
|
\&\s-1OS\s0 to \s-1OS\s0, but I assume you can figure it out if it works different on
|
|
your's. Make sure you do not overwrite any file on your system when
|
|
executing the following command and type the whole line as one long
|
|
line (I had to split it for readability)
|
|
and skip all of the '\e' characters.
|
|
.PP
|
|
.Vb 5
|
|
\& rrdtool create test.rrd \e
|
|
\& \-\-start 920804400 \e
|
|
\& DS:speed:COUNTER:600:U:U \e
|
|
\& RRA:AVERAGE:0.5:1:24 \e
|
|
\& RRA:AVERAGE:0.5:6:10
|
|
.Ve
|
|
.PP
|
|
(So enter: \f(CW\*(C`rrdtool create test.rrd \-\-start 920804400 DS ...\*(C'\fR)
|
|
.SS "What has been created?"
|
|
.IX Subsection "What has been created?"
|
|
We created the round robin database called test (test.rrd) which starts at
|
|
noon the day I started writing this document, 7th of March, 1999 (this date
|
|
translates to 920804400 seconds as explained below). Our database holds
|
|
one data source (\s-1DS\s0) named \*(L"speed\*(R" that represents a counter. This counter
|
|
is read every five minutes (this is the default therefore you don't have to
|
|
put \f(CW\*(C`\-\-step=300\*(C'\fR). In the same database two round robin archives (RRAs)
|
|
are kept, one averages the data every time it is read (e.g., there's nothing
|
|
to average) and keeps 24 samples (24 times 5 minutes is 2 hours). The other
|
|
averages 6 values (half hour) and contains 10 such averages (e.g. 5 hours).
|
|
.PP
|
|
RRDtool works with special time stamps coming from the \s-1UNIX\s0 world.
|
|
This time stamp is the number of seconds that passed since January
|
|
1st 1970 \s-1UTC\s0. The time stamp value is translated into local time and
|
|
it will therefore look different for different time zones.
|
|
.PP
|
|
Chances are that you are not in the same part of the world as I am.
|
|
This means your time zone is different. In all examples where I talk
|
|
about time, the hours may be wrong for you. This has little effect on
|
|
the results of the examples, just correct the hours while reading.
|
|
As an example: where I will see \*(L"12:05\*(R" the \s-1UK\s0 folks will see \*(L"11:05\*(R".
|
|
.PP
|
|
We now have to fill our database with some numbers. We'll pretend to
|
|
have read the following numbers:
|
|
.PP
|
|
.Vb 10
|
|
\& 12:05 12345 km
|
|
\& 12:10 12357 km
|
|
\& 12:15 12363 km
|
|
\& 12:20 12363 km
|
|
\& 12:25 12363 km
|
|
\& 12:30 12373 km
|
|
\& 12:35 12383 km
|
|
\& 12:40 12393 km
|
|
\& 12:45 12399 km
|
|
\& 12:50 12405 km
|
|
\& 12:55 12411 km
|
|
\& 13:00 12415 km
|
|
\& 13:05 12420 km
|
|
\& 13:10 12422 km
|
|
\& 13:15 12423 km
|
|
.Ve
|
|
.PP
|
|
We fill the database as follows:
|
|
.PP
|
|
.Vb 5
|
|
\& rrdtool update test.rrd 920804700:12345 920805000:12357 920805300:12363
|
|
\& rrdtool update test.rrd 920805600:12363 920805900:12363 920806200:12373
|
|
\& rrdtool update test.rrd 920806500:12383 920806800:12393 920807100:12399
|
|
\& rrdtool update test.rrd 920807400:12405 920807700:12411 920808000:12415
|
|
\& rrdtool update test.rrd 920808300:12420 920808600:12422 920808900:12423
|
|
.Ve
|
|
.PP
|
|
This reads: update our test database with the following numbers
|
|
.PP
|
|
.Vb 2
|
|
\& time 920804700, value 12345
|
|
\& time 920805000, value 12357
|
|
.Ve
|
|
.PP
|
|
etcetera.
|
|
.PP
|
|
As you can see, it is possible to feed more than one value into the
|
|
database in one command. I had to stop at three for readability but
|
|
the real maximum per line is \s-1OS\s0 dependent.
|
|
.PP
|
|
We can now retrieve the data from our database using \*(L"rrdtool fetch\*(R":
|
|
.PP
|
|
.Vb 1
|
|
\& rrdtool fetch test.rrd AVERAGE \-\-start 920804400 \-\-end 920809200
|
|
.Ve
|
|
.PP
|
|
It should return the following output:
|
|
.PP
|
|
.Vb 1
|
|
\& speed
|
|
\&
|
|
\& 920804700: nan
|
|
\& 920805000: 4.0000000000e\-02
|
|
\& 920805300: 2.0000000000e\-02
|
|
\& 920805600: 0.0000000000e+00
|
|
\& 920805900: 0.0000000000e+00
|
|
\& 920806200: 3.3333333333e\-02
|
|
\& 920806500: 3.3333333333e\-02
|
|
\& 920806800: 3.3333333333e\-02
|
|
\& 920807100: 2.0000000000e\-02
|
|
\& 920807400: 2.0000000000e\-02
|
|
\& 920807700: 2.0000000000e\-02
|
|
\& 920808000: 1.3333333333e\-02
|
|
\& 920808300: 1.6666666667e\-02
|
|
\& 920808600: 6.6666666667e\-03
|
|
\& 920808900: 3.3333333333e\-03
|
|
\& 920809200: nan
|
|
\& 920809500: nan
|
|
.Ve
|
|
.PP
|
|
Note that you might get more rows than you expect. The reason for this is
|
|
that you ask for a time range that ends on 920809200. The number that is
|
|
written behind 920809200: in the list above covers the time range from
|
|
920808900 to 920809200, \s-1EXCLUDING\s0 920809200. Hence to be on the sure side,
|
|
you receive the entry from 920809200 to 920809500 as well since it \s-1INCLUDES\s0
|
|
920809200. You may also see \*(L"NaN\*(R" instead of \*(L"nan\*(R" this is \s-1OS\s0 dependent.
|
|
\&\*(L"NaN\*(R" stands for \*(L"Not A Number\*(R". If your \s-1OS\s0 writes \*(L"U\*(R" or \*(L"\s-1UNKN\s0\*(R" or
|
|
something similar that's okay. If something else is wrong, it will probably
|
|
be due to an error you made (assuming that my tutorial is correct of course
|
|
:\-). In that case: delete the database and try again.
|
|
.PP
|
|
The meaning of the above output will become clear below.
|
|
.SS "Time to create some graphics"
|
|
.IX Subsection "Time to create some graphics"
|
|
Try the following command:
|
|
.PP
|
|
.Vb 4
|
|
\& rrdtool graph speed.png \e
|
|
\& \-\-start 920804400 \-\-end 920808000 \e
|
|
\& DEF:myspeed=test.rrd:speed:AVERAGE \e
|
|
\& LINE2:myspeed#FF0000
|
|
.Ve
|
|
.PP
|
|
This will create speed.png which starts at 12:00 and ends at 13:00.
|
|
There is a definition of a variable called myspeed, using the data from \s-1RRA\s0
|
|
\&\*(L"speed\*(R" out of database \*(L"test.rrd\*(R". The line drawn is 2 pixels high
|
|
and represents the variable myspeed. The color is red (specified by
|
|
its rgb-representation, see below).
|
|
.PP
|
|
You'll notice that the start of the graph is not at 12:00 but at 12:05.
|
|
This is because we have insufficient data to tell the average before
|
|
that time. This will only happen when you miss some samples, this will
|
|
not happen a lot, hopefully.
|
|
.PP
|
|
If this has worked: congratulations! If not, check what went wrong.
|
|
.PP
|
|
The colors are built up from red, green and blue. For each of the
|
|
components, you specify how much to use in hexadecimal where 00 means
|
|
not included and \s-1FF\s0 means fully included.
|
|
The \*(L"color\*(R" white is a mixture of red, green and blue: \s-1FFFFFF\s0
|
|
The \*(L"color\*(R" black is all colors off: 000000
|
|
.PP
|
|
.Vb 5
|
|
\& red #FF0000
|
|
\& green #00FF00
|
|
\& blue #0000FF
|
|
\& magenta #FF00FF (mixed red with blue)
|
|
\& gray #555555 (one third of all components)
|
|
.Ve
|
|
.PP
|
|
Additionally you can (with a recent RRDtool) add an alpha channel
|
|
(transparency). The default will be \*(L"\s-1FF\s0\*(R" which means non-transparent.
|
|
.PP
|
|
The \s-1PNG\s0 you just created can be displayed using your favorite image
|
|
viewer. Web browsers will display the \s-1PNG\s0 via the \s-1URL\s0
|
|
\&\*(L"file:///the/path/to/speed.png\*(R"
|
|
.SS "Graphics with some math"
|
|
.IX Subsection "Graphics with some math"
|
|
When looking at the image, you notice that the horizontal axis is labeled
|
|
12:10, 12:20, 12:30, 12:40 and 12:50. Sometimes a label doesn't fit (12:00
|
|
and 13:00 would be likely candidates) so they are skipped.
|
|
.PP
|
|
The vertical axis displays the range we entered. We provided
|
|
kilometers and when divided by 300 seconds, we get very small
|
|
numbers. To be exact, the first value was 12 (12357\-12345) and divided
|
|
by 300 this makes 0.04, which is displayed by RRDtool as \*(L"40 m\*(R"
|
|
meaning \*(L"40/1000\*(R". The \*(L"m\*(R" (milli) has nothing to do with meters (also m),
|
|
kilometers or millimeters! RRDtool doesn't know about the physical
|
|
units of our data, it just works with dimensionless numbers.
|
|
.PP
|
|
If we had measured our distances in meters, this would have been
|
|
(12357000\-12345000)/300 = 12000/300 = 40.
|
|
.PP
|
|
As most people have a better feel for numbers in this range, we'll
|
|
correct that. We could recreate our database and store the correct
|
|
data, but there is a better way: we do some calculations while creating
|
|
the png file!
|
|
.PP
|
|
.Vb 6
|
|
\& rrdtool graph speed2.png \e
|
|
\& \-\-start 920804400 \-\-end 920808000 \e
|
|
\& \-\-vertical\-label m/s \e
|
|
\& DEF:myspeed=test.rrd:speed:AVERAGE \e
|
|
\& CDEF:realspeed=myspeed,1000,\e* \e
|
|
\& LINE2:realspeed#FF0000
|
|
.Ve
|
|
.PP
|
|
Note: I need to escape the multiplication operator * with a backslash.
|
|
If I don't, the operating system may interpret it and use it for file
|
|
name expansion. You could also place the line within quotation marks
|
|
like so:
|
|
.PP
|
|
.Vb 1
|
|
\& "CDEF:realspeed=myspeed,1000,*" \e
|
|
.Ve
|
|
.PP
|
|
It boils down to: it is RRDtool which should see *, not your shell.
|
|
And it is your shell interpreting \e, not RRDtool. You may need to
|
|
adjust examples accordingly if you happen to use an operating
|
|
system or shell which behaves differently.
|
|
.PP
|
|
After viewing this \s-1PNG\s0, you notice the \*(L"m\*(R" (milli) has
|
|
disappeared. This it what the correct result would be. Also, a label
|
|
has been added to the image. Apart from the things mentioned above,
|
|
the \s-1PNG\s0 should look the same.
|
|
.PP
|
|
The calculations are specified in the \s-1CDEF\s0 part above and are in
|
|
Reverse Polish Notation (\*(L"\s-1RPN\s0\*(R"). What we requested RRDtool to do is:
|
|
\&\*(L"take the data source myspeed and the number 1000; multiply
|
|
those\*(R". Don't bother with \s-1RPN\s0 yet, it will be explained later on in
|
|
more detail. Also, you may want to read my tutorial on CDEFs and Steve
|
|
Rader's tutorial on \s-1RPN\s0. But first finish this tutorial.
|
|
.PP
|
|
Hang on! If we can multiply values with 1000, it should also be possible
|
|
to display kilometers per hour from the same data!
|
|
.PP
|
|
To change a value that is measured in meters per second:
|
|
.PP
|
|
.Vb 3
|
|
\& Calculate meters per hour: value * 3600
|
|
\& Calculate kilometers per hour: value / 1000
|
|
\& Together this makes: value * (3600/1000) or value * 3.6
|
|
.Ve
|
|
.PP
|
|
In our example database we made a mistake and we need to compensate for
|
|
this by multiplying with 1000. Applying that correction:
|
|
.PP
|
|
.Vb 1
|
|
\& value * 3.6 * 1000 == value * 3600
|
|
.Ve
|
|
.PP
|
|
Now let's create this \s-1PNG\s0, and add some more magic ...
|
|
.PP
|
|
.Vb 10
|
|
\& rrdtool graph speed3.png \e
|
|
\& \-\-start 920804400 \-\-end 920808000 \e
|
|
\& \-\-vertical\-label km/h \e
|
|
\& DEF:myspeed=test.rrd:speed:AVERAGE \e
|
|
\& "CDEF:kmh=myspeed,3600,*" \e
|
|
\& CDEF:fast=kmh,100,GT,kmh,0,IF \e
|
|
\& CDEF:good=kmh,100,GT,0,kmh,IF \e
|
|
\& HRULE:100#0000FF:"Maximum allowed" \e
|
|
\& AREA:good#00FF00:"Good speed" \e
|
|
\& AREA:fast#FF0000:"Too fast"
|
|
.Ve
|
|
.PP
|
|
Note: here we use another means to escape the * operator by enclosing
|
|
the whole string in double quotes.
|
|
.PP
|
|
This graph looks much better. Speed is shown in km/h and there is even
|
|
an extra line with the maximum allowed speed (on the road I travel
|
|
on). I also changed the colors used to display speed and changed it
|
|
from a line into an area.
|
|
.PP
|
|
The calculations are more complex now. For speed measurements within
|
|
the speed limit they are:
|
|
.PP
|
|
.Vb 2
|
|
\& Check if kmh is greater than 100 ( kmh,100 ) GT
|
|
\& If so, return 0, else kmh ((( kmh,100 ) GT ), 0, kmh) IF
|
|
.Ve
|
|
.PP
|
|
For values above the speed limit:
|
|
.PP
|
|
.Vb 2
|
|
\& Check if kmh is greater than 100 ( kmh,100 ) GT
|
|
\& If so, return kmh, else return 0 ((( kmh,100) GT ), kmh, 0) IF
|
|
.Ve
|
|
.SS "Graphics Magic"
|
|
.IX Subsection "Graphics Magic"
|
|
I like to believe there are virtually no limits to how RRDtool graph
|
|
can manipulate data. I will not explain how it works, but look at the
|
|
following \s-1PNG:\s0
|
|
.PP
|
|
.Vb 10
|
|
\& rrdtool graph speed4.png \e
|
|
\& \-\-start 920804400 \-\-end 920808000 \e
|
|
\& \-\-vertical\-label km/h \e
|
|
\& DEF:myspeed=test.rrd:speed:AVERAGE \e
|
|
\& CDEF:nonans=myspeed,UN,0,myspeed,IF \e
|
|
\& CDEF:kmh=nonans,3600,* \e
|
|
\& CDEF:fast=kmh,100,GT,100,0,IF \e
|
|
\& CDEF:over=kmh,100,GT,kmh,100,\-,0,IF \e
|
|
\& CDEF:good=kmh,100,GT,0,kmh,IF \e
|
|
\& HRULE:100#0000FF:"Maximum allowed" \e
|
|
\& AREA:good#00FF00:"Good speed" \e
|
|
\& AREA:fast#550000:"Too fast" \e
|
|
\& STACK:over#FF0000:"Over speed"
|
|
.Ve
|
|
.PP
|
|
Remember the note in the beginning? I had to remove unknown data from
|
|
this example. The 'nonans' \s-1CDEF\s0 is new, and the 6th line (which used to
|
|
be the 5th line) used to read 'CDEF:kmh=myspeed,3600,*'
|
|
.PP
|
|
Let's create a quick and dirty \s-1HTML\s0 page to view the three PNGs:
|
|
.PP
|
|
.Vb 7
|
|
\& <HTML><HEAD><TITLE>Speed</TITLE></HEAD><BODY>
|
|
\& <IMG src="speed2.png" alt="Speed in meters per second">
|
|
\& <BR>
|
|
\& <IMG src="speed3.png" alt="Speed in kilometers per hour">
|
|
\& <BR>
|
|
\& <IMG src="speed4.png" alt="Traveled too fast?">
|
|
\& </BODY></HTML>
|
|
.Ve
|
|
.PP
|
|
Name the file \*(L"speed.html\*(R" or similar, and look at it in your web browser.
|
|
.PP
|
|
Now, all you have to do is measure the values regularly and update the
|
|
database. When you want to view the data, recreate the PNGs and make
|
|
sure to refresh them in your browser. (Note: just clicking reload may
|
|
not be enough, especially when proxies are involved. Try shift-reload
|
|
or ctrl\-F5).
|
|
.SS "Updates in Reality"
|
|
.IX Subsection "Updates in Reality"
|
|
We've already used the \f(CW\*(C`update\*(C'\fR command: it took one or more
|
|
parameters in the form of \*(L"<time>:<value>\*(R". You'll be glad to know
|
|
that you can specify the current time by filling in a \*(L"N\*(R" as the time.
|
|
Or you could use the \*(L"time\*(R" function in Perl (the shortest example in
|
|
this tutorial):
|
|
.PP
|
|
.Vb 1
|
|
\& perl \-e \*(Aqprint time, "\en" \*(Aq
|
|
.Ve
|
|
.PP
|
|
How to run a program on regular intervals is \s-1OS\s0 specific. But here is
|
|
an example in pseudo code:
|
|
.PP
|
|
.Vb 2
|
|
\& \- Get the value and put it in variable "$speed"
|
|
\& \- rrdtool update speed.rrd N:$speed
|
|
.Ve
|
|
.PP
|
|
(do not try this with our test database, we'll use it in further examples)
|
|
.PP
|
|
This is all. Run the above script every five minutes. When you need to know
|
|
what the graphs look like, run the examples above. You could put them
|
|
in a script as well. After running that script, view the page
|
|
index.html we created above.
|
|
.SS "Some words on \s-1SNMP\s0"
|
|
.IX Subsection "Some words on SNMP"
|
|
I can imagine very few people that will be able to get real data from
|
|
their car every five minutes. All other people will have to settle for
|
|
some other kind of counter. You could measure the number of pages
|
|
printed by a printer, for example, the cups of coffee made by the
|
|
coffee machine, a device that counts the electricity used,
|
|
whatever. Any incrementing counter can be monitored and graphed using
|
|
the stuff you learned so far. Later on we will also be able to monitor
|
|
other types of values like temperature.
|
|
.PP
|
|
Many people interested in RRDtool will use the counter that keeps track
|
|
of octets (bytes) transferred by a network device. So let's do just
|
|
that next. We will start with a description of how to collect data.
|
|
.PP
|
|
Some people will make a remark that there are tools which can do this data
|
|
collection for you. They are right! However, I feel it is important that
|
|
you understand they are not necessary. When you have to determine why
|
|
things went wrong you need to know how they work.
|
|
.PP
|
|
One tool used in the example has been talked about very briefly in the
|
|
beginning of this document, it is called \s-1SNMP\s0. It is a way of talking
|
|
to networked equipment. The tool I use below is called \*(L"snmpget\*(R" and
|
|
this is how it works:
|
|
.PP
|
|
.Vb 1
|
|
\& snmpget device password OID
|
|
.Ve
|
|
.PP
|
|
or
|
|
.PP
|
|
.Vb 1
|
|
\& snmpget \-v[version] \-c[password] device OID
|
|
.Ve
|
|
.PP
|
|
For device you substitute the name, or the \s-1IP\s0 address, of your device.
|
|
For password you use the \*(L"community read string\*(R" as it is called in the
|
|
\&\s-1SNMP\s0 world. For some devices the default of \*(L"public\*(R" might work, however
|
|
this can be disabled, altered or protected for privacy and security
|
|
reasons. Read the documentation that comes with your device or program.
|
|
.PP
|
|
Then there is this parameter, called \s-1OID\s0, which means \*(L"object identifier\*(R".
|
|
.PP
|
|
When you start to learn about \s-1SNMP\s0 it looks very confusing. It isn't
|
|
all that difficult when you look at the Management Information Base
|
|
(\*(L"\s-1MIB\s0\*(R"). It is an upside-down tree that describes data, with a single node
|
|
as the root and from there a number of branches. These branches end
|
|
up in another node, they branch out, etc. All the branches have a name
|
|
and they form the path that we follow all the way down. The branches
|
|
that we follow are named: iso, org, dod, internet, mgmt and mib\-2.
|
|
These names can also be written down as numbers and are 1 3 6 1 2 1.
|
|
.PP
|
|
.Vb 1
|
|
\& iso.org.dod.internet.mgmt.mib\-2 (1.3.6.1.2.1)
|
|
.Ve
|
|
.PP
|
|
There is a lot of confusion about the leading dot that some programs
|
|
use. There is *no* leading dot in an \s-1OID\s0. However, some programs
|
|
can use the above part of OIDs as a default. To indicate the difference
|
|
between abbreviated OIDs and full OIDs they need a leading dot when
|
|
you specify the complete \s-1OID\s0. Often those programs will leave out
|
|
the default portion when returning the data to you. To make things
|
|
worse, they have several default prefixes ...
|
|
.PP
|
|
Ok, lets continue to the start of our \s-1OID:\s0 we had 1.3.6.1.2.1
|
|
From there, we are especially interested in the branch \*(L"interfaces\*(R"
|
|
which has number 2 (e.g., 1.3.6.1.2.1.2 or 1.3.6.1.2.1.interfaces).
|
|
.PP
|
|
First, we have to get some \s-1SNMP\s0 program. First look if there is a
|
|
pre-compiled package available for your \s-1OS\s0. This is the preferred way.
|
|
If not, you will have to get the sources yourself and compile those.
|
|
The Internet is full of sources, programs etc. Find information using
|
|
a search engine or whatever you prefer.
|
|
.PP
|
|
Assume you got the program. First try to collect some data that is
|
|
available on most systems. Remember: there is a short name for the
|
|
part of the tree that interests us most in the world we live in!
|
|
.PP
|
|
I will give an example which can be used on Fedora Core 3. If it
|
|
doesn't work for you, work your way through the manual of snmp and
|
|
adapt the example to make it work.
|
|
.PP
|
|
.Vb 1
|
|
\& snmpget \-v2c \-c public myrouter system.sysDescr.0
|
|
.Ve
|
|
.PP
|
|
The device should answer with a description of itself, perhaps an
|
|
empty one. Until you got a valid answer from a device, perhaps using a
|
|
different \*(L"password\*(R", or a different device, there is no point in
|
|
continuing.
|
|
.PP
|
|
.Vb 1
|
|
\& snmpget \-v2c \-c public myrouter interfaces.ifNumber.0
|
|
.Ve
|
|
.PP
|
|
Hopefully you get a number as a result, the number of interfaces.
|
|
If so, you can carry on and try a different program called \*(L"snmpwalk\*(R".
|
|
.PP
|
|
.Vb 1
|
|
\& snmpwalk \-v2c \-c public myrouter interfaces.ifTable.ifEntry.ifDescr
|
|
.Ve
|
|
.PP
|
|
If it returns with a list of interfaces, you're almost there.
|
|
Here's an example:
|
|
[user@host /home/alex]$ snmpwalk \-v2c \-c public cisco 2.2.1.2
|
|
.PP
|
|
.Vb 5
|
|
\& interfaces.ifTable.ifEntry.ifDescr.1 = "BRI0: B\-Channel 1"
|
|
\& interfaces.ifTable.ifEntry.ifDescr.2 = "BRI0: B\-Channel 2"
|
|
\& interfaces.ifTable.ifEntry.ifDescr.3 = "BRI0" Hex: 42 52 49 30
|
|
\& interfaces.ifTable.ifEntry.ifDescr.4 = "Ethernet0"
|
|
\& interfaces.ifTable.ifEntry.ifDescr.5 = "Loopback0"
|
|
.Ve
|
|
.PP
|
|
On this cisco equipment, I would like to monitor the \*(L"Ethernet0\*(R"
|
|
interface and from the above output I see that it is number four. I try:
|
|
.PP
|
|
.Vb 1
|
|
\& [user@host /home/alex]$ snmpget \-v2c \-c public cisco 2.2.1.10.4 2.2.1.16.4
|
|
\&
|
|
\& interfaces.ifTable.ifEntry.ifInOctets.4 = 2290729126
|
|
\& interfaces.ifTable.ifEntry.ifOutOctets.4 = 1256486519
|
|
.Ve
|
|
.PP
|
|
So now I have two OIDs to monitor and they are (in full, this time):
|
|
.PP
|
|
.Vb 1
|
|
\& 1.3.6.1.2.1.2.2.1.10
|
|
.Ve
|
|
.PP
|
|
and
|
|
.PP
|
|
.Vb 1
|
|
\& 1.3.6.1.2.1.2.2.1.16
|
|
.Ve
|
|
.PP
|
|
both with an interface number of 4.
|
|
.PP
|
|
Don't get fooled, this wasn't my first try. It took some time for me too
|
|
to understand what all these numbers mean. It does help a lot when they
|
|
get translated into descriptive text... At least, when people are talking
|
|
about MIBs and OIDs you know what it's all about.
|
|
Do not forget the interface number (0 if it is not interface dependent)
|
|
and try snmpwalk if you don't get an answer from snmpget.
|
|
.PP
|
|
If you understand the above section and get numbers from your device, continue
|
|
on with this tutorial. If not, then go back and re-read this part.
|
|
.SS "A Real World Example"
|
|
.IX Subsection "A Real World Example"
|
|
Let the fun begin. First, create a new database. It contains data from
|
|
two counters, called input and output. The data is put into archives
|
|
that average it. They take 1, 6, 24 or 288 samples at a time.
|
|
They also go into archives that keep the maximum numbers. This will be
|
|
explained later on. The time in-between samples is 300 seconds, a good
|
|
starting point, which is the same as five minutes.
|
|
.PP
|
|
.Vb 4
|
|
\& 1 sample "averaged" stays 1 period of 5 minutes
|
|
\& 6 samples averaged become one average on 30 minutes
|
|
\& 24 samples averaged become one average on 2 hours
|
|
\& 288 samples averaged become one average on 1 day
|
|
.Ve
|
|
.PP
|
|
Lets try to be compatible with \s-1MRTG\s0 which stores about the following
|
|
amount of data:
|
|
.PP
|
|
.Vb 4
|
|
\& 600 5\-minute samples: 2 days and 2 hours
|
|
\& 600 30\-minute samples: 12.5 days
|
|
\& 600 2\-hour samples: 50 days
|
|
\& 732 1\-day samples: 732 days
|
|
.Ve
|
|
.PP
|
|
These ranges are appended, so the total amount of data stored in the
|
|
database is approximately 797 days. RRDtool stores the data
|
|
differently, it doesn't start the \*(L"weekly\*(R" archive where the \*(L"daily\*(R"
|
|
archive stopped. For both archives the most recent data will be near
|
|
\&\*(L"now\*(R" and therefore we will need to keep more data than \s-1MRTG\s0 does!
|
|
.PP
|
|
We will need:
|
|
.PP
|
|
.Vb 4
|
|
\& 600 samples of 5 minutes (2 days and 2 hours)
|
|
\& 700 samples of 30 minutes (2 days and 2 hours, plus 12.5 days)
|
|
\& 775 samples of 2 hours (above + 50 days)
|
|
\& 797 samples of 1 day (above + 732 days, rounded up to 797)
|
|
\&
|
|
\& rrdtool create myrouter.rrd \e
|
|
\& DS:input:COUNTER:600:U:U \e
|
|
\& DS:output:COUNTER:600:U:U \e
|
|
\& RRA:AVERAGE:0.5:1:600 \e
|
|
\& RRA:AVERAGE:0.5:6:700 \e
|
|
\& RRA:AVERAGE:0.5:24:775 \e
|
|
\& RRA:AVERAGE:0.5:288:797 \e
|
|
\& RRA:MAX:0.5:1:600 \e
|
|
\& RRA:MAX:0.5:6:700 \e
|
|
\& RRA:MAX:0.5:24:775 \e
|
|
\& RRA:MAX:0.5:288:797
|
|
.Ve
|
|
.PP
|
|
Next thing to do is to collect data and store it. Here is an example.
|
|
It is written partially in pseudo code, you will have to find out what
|
|
to do exactly on your \s-1OS\s0 to make it work.
|
|
.PP
|
|
.Vb 8
|
|
\& while not the end of the universe
|
|
\& do
|
|
\& get result of
|
|
\& snmpget router community 2.2.1.10.4
|
|
\& into variable $in
|
|
\& get result of
|
|
\& snmpget router community 2.2.1.16.4
|
|
\& into variable $out
|
|
\&
|
|
\& rrdtool update myrouter.rrd N:$in:$out
|
|
\&
|
|
\& wait for 5 minutes
|
|
\& done
|
|
.Ve
|
|
.PP
|
|
Then, after collecting data for a day, try to create an image using:
|
|
.PP
|
|
.Vb 5
|
|
\& rrdtool graph myrouter\-day.png \-\-start \-86400 \e
|
|
\& DEF:inoctets=myrouter.rrd:input:AVERAGE \e
|
|
\& DEF:outoctets=myrouter.rrd:output:AVERAGE \e
|
|
\& AREA:inoctets#00FF00:"In traffic" \e
|
|
\& LINE1:outoctets#0000FF:"Out traffic"
|
|
.Ve
|
|
.PP
|
|
This should produce a picture with one day worth of traffic.
|
|
One day is 24 hours of 60 minutes of 60 seconds: 24*60*60=86400, we
|
|
start at now minus 86400 seconds. We define (with DEFs) inoctets and
|
|
outoctets as the average values from the database myrouter.rrd and draw
|
|
an area for the \*(L"in\*(R" traffic and a line for the \*(L"out\*(R" traffic.
|
|
.PP
|
|
View the image and keep logging data for a few more days.
|
|
If you like, you could try the examples from the test database and
|
|
see if you can get various options and calculations to work.
|
|
.PP
|
|
Suggestion: Display in bytes per second and in bits per second. Make
|
|
the Ethernet graphics go red if they are over four megabits per
|
|
second.
|
|
.SS "Consolidation Functions"
|
|
.IX Subsection "Consolidation Functions"
|
|
A few paragraphs back I mentioned the possibility of keeping
|
|
the maximum values instead of the average values. Let's go
|
|
into this a bit more.
|
|
.PP
|
|
Recall all the stuff about the speed of the car. Suppose we drove at 144
|
|
km/h during 5 minutes and then were stopped by the police for 25 minutes.
|
|
At the end of the lecture we would take our laptop and create and view the
|
|
image taken from the database. If we look at the second \s-1RRA\s0 we did
|
|
create, we would have the average from 6 samples. The samples measured
|
|
would be 144+0+0+0+0+0=144, divided by 30 minutes, corrected for the
|
|
error by 1000, translated into km/h, with a result of 24 km/h.
|
|
I would still get a ticket but not for speeding anymore :)
|
|
.PP
|
|
Obviously, in this case we shouldn't look at the averages. In some
|
|
cases they are handy. If you want to know how many km you had traveled,
|
|
the averaged picture would be the right one to look at. On the other hand, for
|
|
the speed that we traveled at, the maximum numbers seen is much more
|
|
interesting. Later we will see more types.
|
|
.PP
|
|
It is the same for data. If you want to know the amount, look at the
|
|
averages. If you want to know the rate, look at the maximum.
|
|
Over time, they will grow apart more and more. In the last database
|
|
we have created, there are two archives that keep data per day. The
|
|
archive that keeps averages will show low numbers, the archive that
|
|
shows maxima will have higher numbers.
|
|
.PP
|
|
For my car this would translate in averages per day of 96/24=4 km/h
|
|
(as I travel about 94 kilometers on a day) during working days, and
|
|
maxima of 120 km/h (my top speed that I reach every day).
|
|
.PP
|
|
Big difference. Do not look at the second graph to estimate the
|
|
distances that I travel and do not look at the first graph to
|
|
estimate my speed. This will work if the samples are close together,
|
|
as they are in five minutes, but not if you average.
|
|
.PP
|
|
On some days, I go for a long ride. If I go across Europe and travel
|
|
for 12 hours, the first graph will rise to about 60 km/h. The second
|
|
one will show 180 km/h. This means that I traveled a distance of 60
|
|
km/h times 24 h = 1440 km. I did this with a higher speed and a
|
|
maximum around 180 km/h. However, it probably doesn't mean that I
|
|
traveled for 8 hours at a constant speed of 180 km/h!
|
|
.PP
|
|
This is a real example: go with the flow through Germany (fast!) and stop
|
|
a few times for gas and coffee. Drive slowly through Austria and the
|
|
Netherlands. Be careful in the mountains and villages. If you would
|
|
look at the graphs created from the five-minute averages you would
|
|
get a totally different picture. You would see the same values on the
|
|
average and maximum graphs (provided I measured every 300 seconds).
|
|
You would be able to see when I stopped, when I was in top gear, when
|
|
I drove over fast highways etc. The granularity of the data is much
|
|
higher, so you can see more. However, this takes 12 samples per hour,
|
|
or 288 values per day, so it would be a lot of data over a longer
|
|
period of time. Therefore we average it, eventually to one value per
|
|
day. From this one value, we cannot see much detail, of course.
|
|
.PP
|
|
Make sure you understand the last few paragraphs. There is no value
|
|
in only a line and a few axis, you need to know what they mean and
|
|
interpret the data in an appropriate way. This is true for all data.
|
|
.PP
|
|
The biggest mistake you can make is to use the collected data for
|
|
something that it is not suitable for. You would be better off if
|
|
you didn't have the graph at all.
|
|
.SS "Let's review what you now should know"
|
|
.IX Subsection "Let's review what you now should know"
|
|
You know how to create a database and can put data in it. You can get
|
|
the numbers out again by creating an image, do math on the data from
|
|
the database and view the result instead of the raw data. You know
|
|
about the difference between averages and maximum, and when to use
|
|
which (or at least you should have an idea).
|
|
.PP
|
|
RRDtool can do more than what we have learned up to now. Before you
|
|
continue with the rest of this doc, I recommend that you reread from
|
|
the start and try some modifications on the examples. Make sure you
|
|
fully understand everything. It will be worth the effort and helps
|
|
you not only with the rest of this tutorial, but also in your day to day
|
|
monitoring long after you read this introduction.
|
|
.SS "Data Source Types"
|
|
.IX Subsection "Data Source Types"
|
|
All right, you feel like continuing. Welcome back and get ready
|
|
for an increased speed in the examples and explanations.
|
|
.PP
|
|
You know that in order to view a counter over time, you have to
|
|
take two numbers and divide the difference of them between the
|
|
time lapsed. This makes sense for the examples I gave you but there
|
|
are other possibilities. For instance, I'm able to retrieve the
|
|
temperature from my router in three places namely the inlet, the
|
|
so called hot-spot and the exhaust. These values are not counters.
|
|
If I take the difference of the two samples and divide that by
|
|
300 seconds I would be asking for the temperature change per second.
|
|
Hopefully this is zero! If not, the computer room is probably on fire :)
|
|
.PP
|
|
So, what can we do? We can tell RRDtool to store the values we measure
|
|
directly as they are (this is not entirely true but close enough). The
|
|
graphs we make will look much better, they will show a rather constant
|
|
value. I know when the router is busy (it
|
|
works \-> it uses more electricity \-> it generates more heat \-> the
|
|
temperature rises). I know when the doors are left open (the room is
|
|
air conditioned) \-> the warm air from the rest of the building flows into the
|
|
computer room \-> the inlet temperature rises). Etc. The data type we
|
|
use when creating the database before was counter, we now have a
|
|
different data type and thus a different name for it. It is called
|
|
\&\s-1GAUGE\s0. There are more such data types:
|
|
.PP
|
|
.Vb 4
|
|
\& \- COUNTER we already know this one
|
|
\& \- GAUGE we just learned this one
|
|
\& \- DERIVE
|
|
\& \- ABSOLUTE
|
|
.Ve
|
|
.PP
|
|
The two additional types are \s-1DERIVE\s0 and \s-1ABSOLUTE\s0. Absolute can be used like
|
|
counter with one difference: RRDtool assumes the counter is reset when
|
|
it's read. That is: its delta is known without calculation by RRDtool
|
|
whereas RRDtool needs to calculate it for the counter type.
|
|
Example: our first example (12345, 12357, 12363, 12363) would read:
|
|
unknown, 12, 6, 0. The rest of the calculations stay the same.
|
|
The other one, derive, is like counter. Unlike counter, it can also
|
|
decrease so it can have a negative delta. Again, the rest of the
|
|
calculations stay the same.
|
|
.PP
|
|
Let's try them all:
|
|
.PP
|
|
.Vb 10
|
|
\& rrdtool create all.rrd \-\-start 978300900 \e
|
|
\& DS:a:COUNTER:600:U:U \e
|
|
\& DS:b:GAUGE:600:U:U \e
|
|
\& DS:c:DERIVE:600:U:U \e
|
|
\& DS:d:ABSOLUTE:600:U:U \e
|
|
\& RRA:AVERAGE:0.5:1:10
|
|
\& rrdtool update all.rrd \e
|
|
\& 978301200:300:1:600:300 \e
|
|
\& 978301500:600:3:1200:600 \e
|
|
\& 978301800:900:5:1800:900 \e
|
|
\& 978302100:1200:3:2400:1200 \e
|
|
\& 978302400:1500:1:2400:1500 \e
|
|
\& 978302700:1800:2:1800:1800 \e
|
|
\& 978303000:2100:4:0:2100 \e
|
|
\& 978303300:2400:6:600:2400 \e
|
|
\& 978303600:2700:4:600:2700 \e
|
|
\& 978303900:3000:2:1200:3000
|
|
\& rrdtool graph all1.png \-s 978300600 \-e 978304200 \-h 400 \e
|
|
\& DEF:linea=all.rrd:a:AVERAGE LINE3:linea#FF0000:"Line A" \e
|
|
\& DEF:lineb=all.rrd:b:AVERAGE LINE3:lineb#00FF00:"Line B" \e
|
|
\& DEF:linec=all.rrd:c:AVERAGE LINE3:linec#0000FF:"Line C" \e
|
|
\& DEF:lined=all.rrd:d:AVERAGE LINE3:lined#000000:"Line D"
|
|
.Ve
|
|
.SS "RRDtool under the Microscope"
|
|
.IX Subsection "RRDtool under the Microscope"
|
|
.IP "\(bu" 2
|
|
Line A is a \s-1COUNTER\s0 type, so it should continuously increment and RRDtool
|
|
must calculate the differences. Also, RRDtool needs to divide the
|
|
difference by the amount of time lapsed. This should end up as a
|
|
straight line at 1 (the deltas are 300, the time is 300).
|
|
.IP "\(bu" 2
|
|
Line B is of type \s-1GAUGE\s0. These are \*(L"real\*(R" values so they should match
|
|
what we put in: a sort of a wave.
|
|
.IP "\(bu" 2
|
|
Line C is of type \s-1DERIVE\s0. It should be a counter that can decrease. It does
|
|
so between 2400 and 0, with 1800 in-between.
|
|
.IP "\(bu" 2
|
|
Line D is of type \s-1ABSOLUTE\s0. This is like counter but it works on
|
|
values without calculating the difference. The numbers are the same
|
|
and as you can see (hopefully) this has a different result.
|
|
.PP
|
|
This translates in the following values, starting at 23:10 and ending
|
|
at 00:10 the next day (where \*(L"u\*(R" means unknown/unplotted):
|
|
.PP
|
|
.Vb 4
|
|
\& \- Line A: u u 1 1 1 1 1 1 1 1 1 u
|
|
\& \- Line B: u 1 3 5 3 1 2 4 6 4 2 u
|
|
\& \- Line C: u u 2 2 2 0 \-2 \-6 2 0 2 u
|
|
\& \- Line D: u 1 2 3 4 5 6 7 8 9 10 u
|
|
.Ve
|
|
.PP
|
|
If your \s-1PNG\s0 shows all this, you know you have entered the data correctly,
|
|
the RRDtool executable is working properly, your viewer doesn't fool you,
|
|
and you successfully entered the year 2000 :)
|
|
.PP
|
|
You could try the same example four times, each time with only one of
|
|
the lines.
|
|
.PP
|
|
Let's go over the data again:
|
|
.IP "\(bu" 2
|
|
Line A: 300,600,900 and so on. The counter delta is a constant 300 and
|
|
so is the time delta. A number divided by itself is always 1 (except
|
|
when dividing by zero which is undefined/illegal).
|
|
.Sp
|
|
Why is it that the first point is unknown? We do know what we put into
|
|
the database, right? True, But we didn't have a value to calculate the delta
|
|
from, so we don't know where we started. It would be wrong to assume we
|
|
started at zero so we don't!
|
|
.IP "\(bu" 2
|
|
Line B: There is nothing to calculate. The numbers are as they are.
|
|
.IP "\(bu" 2
|
|
Line C: Again, the start-out value is unknown. The same story is holds
|
|
as for line A. In this case the deltas are not constant, therefore the line
|
|
is not either. If we would put the same numbers in the database as we did for
|
|
line A, we would have gotten the same line. Unlike type counter,
|
|
this type can decrease and I hope to show you later on why
|
|
this makes a difference.
|
|
.IP "\(bu" 2
|
|
Line D: Here the device calculates the deltas. Therefore we \s-1DO\s0 know the
|
|
first delta and it is plotted. We had the same input as with line A, but
|
|
the meaning of this input is different and thus the line is different.
|
|
In this case the deltas increase each time with 300. The time delta
|
|
stays at a constant 300 and therefore the division of the two gives
|
|
increasing values.
|
|
.SS "Counter Wraps"
|
|
.IX Subsection "Counter Wraps"
|
|
There are a few more basics to show. Some important options are still to
|
|
be covered and we haven't look at counter wraps yet. First the counter wrap:
|
|
In our car we notice that the counter shows 999987. We travel 20 km and
|
|
the counter should go to 1000007. Unfortunately, there are only six digits
|
|
on our counter so it really shows 000007. If we would plot that on a type
|
|
\&\s-1DERIVE\s0, it would mean that the counter was set back 999980 km. It wasn't,
|
|
and there has to be some protection for this. This protection is only
|
|
available for type \s-1COUNTER\s0 which should be used for this kind of counter
|
|
anyways. How does it work? Type counter should never decrease and
|
|
therefore RRDtool must assume it wrapped if it does decrease!
|
|
If the delta is negative, this can be compensated for by adding the
|
|
maximum value of the counter + 1. For our car this would be:
|
|
.PP
|
|
.Vb 1
|
|
\& Delta = 7 \- 999987 = \-999980 (instead of 1000007\-999987=20)
|
|
\&
|
|
\& Real delta = \-999980 + 999999 + 1 = 20
|
|
.Ve
|
|
.PP
|
|
At the time of writing this document, RRDtool knows of counters that
|
|
are either 32 bits or 64 bits of size. These counters can handle the
|
|
following different values:
|
|
.PP
|
|
.Vb 2
|
|
\& \- 32 bits: 0 .. 4294967295
|
|
\& \- 64 bits: 0 .. 18446744073709551615
|
|
.Ve
|
|
.PP
|
|
If these numbers look strange to you, you can view them in
|
|
their hexadecimal form:
|
|
.PP
|
|
.Vb 2
|
|
\& \- 32 bits: 0 .. FFFFFFFF
|
|
\& \- 64 bits: 0 .. FFFFFFFFFFFFFFFF
|
|
.Ve
|
|
.PP
|
|
RRDtool handles both counters the same. If an overflow occurs and
|
|
the delta would be negative, RRDtool first adds the maximum of a small
|
|
counter + 1 to the delta. If the delta is still negative, it had to be
|
|
the large counter that wrapped. Add the maximum possible value of the
|
|
large counter + 1 and subtract the erroneously added small value.
|
|
.PP
|
|
There is a risk in this: suppose the large counter wrapped while adding
|
|
a huge delta, it could happen, theoretically, that adding the smaller value
|
|
would make the delta positive. In this unlikely case the results would
|
|
not be correct. The increase should be nearly as high as the maximum
|
|
counter value for that to happen, so chances are you would have several
|
|
other problems as well and this particular problem would not even be
|
|
worth thinking about. Even though, I did include an example, so you
|
|
can judge for yourself.
|
|
.PP
|
|
The next section gives you some numerical examples for counter-wraps.
|
|
Try to do the calculations yourself or just believe me if your calculator
|
|
can't handle the numbers :)
|
|
.PP
|
|
Correction numbers:
|
|
.PP
|
|
.Vb 3
|
|
\& \- 32 bits: (4294967295 + 1) = 4294967296
|
|
\& \- 64 bits: (18446744073709551615 + 1)
|
|
\& \- correction1 = 18446744069414584320
|
|
\&
|
|
\& Before: 4294967200
|
|
\& Increase: 100
|
|
\& Should become: 4294967300
|
|
\& But really is: 4
|
|
\& Delta: \-4294967196
|
|
\& Correction1: \-4294967196 + 4294967296 = 100
|
|
\&
|
|
\& Before: 18446744073709551000
|
|
\& Increase: 800
|
|
\& Should become: 18446744073709551800
|
|
\& But really is: 184
|
|
\& Delta: \-18446744073709550816
|
|
\& Correction1: \-18446744073709550816
|
|
\& + 4294967296 = \-18446744069414583520
|
|
\& Correction2: \-18446744069414583520
|
|
\& + 18446744069414584320 = 800
|
|
\&
|
|
\& Before: 18446744073709551615 ( maximum value )
|
|
\& Increase: 18446744069414584320 ( absurd increase, minimum for
|
|
\& Should become: 36893488143124135935 this example to work )
|
|
\& But really is: 18446744069414584319
|
|
\& Delta: \-4294967296
|
|
\& Correction1: \-4294967296 + 4294967296 = 0
|
|
\& (not negative \-> no correction2)
|
|
\&
|
|
\& Before: 18446744073709551615 ( maximum value )
|
|
\& Increase: 18446744069414584319 ( one less increase )
|
|
\& Should become: 36893488143124135934
|
|
\& But really is: 18446744069414584318
|
|
\& Delta: \-4294967297
|
|
\& Correction1: \-4294967297 + 4294967296 = \-1
|
|
\& Correction2: \-1 + 18446744069414584320 = 18446744069414584319
|
|
.Ve
|
|
.PP
|
|
As you can see from the last two examples, you need strange numbers
|
|
for RRDtool to fail (provided it's bug free of course), so this should
|
|
not happen. However, \s-1SNMP\s0 or whatever method you choose to collect the
|
|
data, might also report wrong numbers occasionally. We can't prevent all
|
|
errors, but there are some things we can do. The RRDtool \*(L"create\*(R" command
|
|
takes two special parameters for this. They define
|
|
the minimum and maximum allowed values. Until now, we used \*(L"U\*(R", meaning
|
|
\&\*(L"unknown\*(R". If you provide values for one or both of them and if RRDtool
|
|
receives data points that are outside these limits, it will ignore those
|
|
values. For a thermometer in degrees Celsius, the absolute minimum is
|
|
just under \-273. For my router, I can assume this minimum is much higher
|
|
so I would set it to 10, where as the maximum temperature I would
|
|
set to 80. Any higher and the device would be out of order.
|
|
.PP
|
|
For the speed of my car, I would never expect negative numbers and
|
|
also I would not expect a speed higher than 230. Anything else,
|
|
and there must have been an error. Remember: the opposite is not true,
|
|
if the numbers pass this check, it doesn't mean that they are
|
|
correct. Always judge the graph with a healthy dose of suspicion if it
|
|
seems weird to you.
|
|
.SS "Data Resampling"
|
|
.IX Subsection "Data Resampling"
|
|
One important feature of RRDtool has not been explained yet: it is
|
|
virtually impossible to collect data and feed it into RRDtool on exact
|
|
intervals. RRDtool therefore interpolates the data, so they are stored
|
|
on exact intervals. If you do not know what this means or how it
|
|
works, then here's the help you seek:
|
|
.PP
|
|
Suppose a counter increases by exactly one for every second. You want
|
|
to measure it in 300 seconds intervals. You should retrieve values
|
|
that are exactly 300 apart. However, due to various circumstances you
|
|
are a few seconds late and the interval is 303. The delta will also be
|
|
303 in that case. Obviously, RRDtool should not put 303 in the database
|
|
and make you believe that the counter increased by 303 in 300 seconds.
|
|
This is where RRDtool interpolates: it alters the 303 value as if it
|
|
would have been stored earlier and it will be 300 in 300 seconds.
|
|
Next time you are at exactly the right time. This means that the current
|
|
interval is 297 seconds and also the counter increased by 297. Again,
|
|
RRDtool interpolates and stores 300 as it should be.
|
|
.PP
|
|
.Vb 1
|
|
\& in the RRD in reality
|
|
\&
|
|
\& time+000: 0 delta="U" time+000: 0 delta="U"
|
|
\& time+300: 300 delta=300 time+300: 300 delta=300
|
|
\& time+600: 600 delta=300 time+603: 603 delta=303
|
|
\& time+900: 900 delta=300 time+900: 900 delta=297
|
|
.Ve
|
|
.PP
|
|
Let's create two identical databases. I've chosen the time range 920805000
|
|
to 920805900 as this goes very well with the example numbers.
|
|
.PP
|
|
.Vb 4
|
|
\& rrdtool create seconds1.rrd \e
|
|
\& \-\-start 920804700 \e
|
|
\& DS:seconds:COUNTER:600:U:U \e
|
|
\& RRA:AVERAGE:0.5:1:24
|
|
.Ve
|
|
.PP
|
|
Make a copy
|
|
.PP
|
|
.Vb 3
|
|
\& for Unix: cp seconds1.rrd seconds2.rrd
|
|
\& for Dos: copy seconds1.rrd seconds2.rrd
|
|
\& for vms: how would I know :)
|
|
.Ve
|
|
.PP
|
|
Put in some data
|
|
.PP
|
|
.Vb 4
|
|
\& rrdtool update seconds1.rrd \e
|
|
\& 920805000:000 920805300:300 920805600:600 920805900:900
|
|
\& rrdtool update seconds2.rrd \e
|
|
\& 920805000:000 920805300:300 920805603:603 920805900:900
|
|
.Ve
|
|
.PP
|
|
Create output
|
|
.PP
|
|
.Vb 10
|
|
\& rrdtool graph seconds1.png \e
|
|
\& \-\-start 920804700 \-\-end 920806200 \e
|
|
\& \-\-height 200 \e
|
|
\& \-\-upper\-limit 1.05 \-\-lower\-limit 0.95 \-\-rigid \e
|
|
\& DEF:seconds=seconds1.rrd:seconds:AVERAGE \e
|
|
\& CDEF:unknown=seconds,UN \e
|
|
\& LINE2:seconds#0000FF \e
|
|
\& AREA:unknown#FF0000
|
|
\& rrdtool graph seconds2.png \e
|
|
\& \-\-start 920804700 \-\-end 920806200 \e
|
|
\& \-\-height 200 \e
|
|
\& \-\-upper\-limit 1.05 \-\-lower\-limit 0.95 \-\-rigid \e
|
|
\& DEF:seconds=seconds2.rrd:seconds:AVERAGE \e
|
|
\& CDEF:unknown=seconds,UN \e
|
|
\& LINE2:seconds#0000FF \e
|
|
\& AREA:unknown#FF0000
|
|
.Ve
|
|
.PP
|
|
View both images together (add them to your index.html file)
|
|
and compare. Both graphs should show the same, despite the
|
|
input being different.
|
|
.SH "WRAPUP"
|
|
.IX Header "WRAPUP"
|
|
It's time now to wrap up this tutorial. We covered all the basics for
|
|
you to be able to work with RRDtool and to read the additional
|
|
documentation available. There is plenty more to discover about
|
|
RRDtool and you will find more and more uses for this package. You can
|
|
easily create graphs using just the examples provided and using only
|
|
RRDtool. You can also use one of the front ends to RRDtool that are
|
|
available.
|
|
.SH "MAILINGLIST"
|
|
.IX Header "MAILINGLIST"
|
|
Remember to subscribe to the RRDtool mailing list. Even if you are not
|
|
answering to mails that come by, it helps both you and the rest of the
|
|
users. A lot of the stuff that I know about \s-1MRTG\s0 (and therefore about
|
|
RRDtool) I've learned while just reading the list without posting to
|
|
it. I did not need to ask the basic questions as they are answered in
|
|
the \s-1FAQ\s0 (read it!) and in various mails by other users. With
|
|
thousands of users all over the world, there will always be people who
|
|
ask questions that you can answer because you read this and other
|
|
documentation and they didn't.
|
|
.SH "SEE ALSO"
|
|
.IX Header "SEE ALSO"
|
|
The RRDtool manpages
|
|
.SH "AUTHOR"
|
|
.IX Header "AUTHOR"
|
|
I hope you enjoyed the examples and their descriptions. If you do, help
|
|
other people by pointing them to this document when they are asking
|
|
basic questions. They will not only get their answers, but at the same
|
|
time learn a whole lot more.
|
|
.PP
|
|
Alex van den Bogaerdt
|
|
<alex@vandenbogaerdt.nl>
|