catalog/INDEX010064400000000000003000000020110772070252700131210ustar00rootsys00000000000000distribution layout_version 1.0 data_model_revision 2.40 uuid 717cb270-d31a-11d7-a58a-00306e49df22 mod_time 1061389655 create_time 1061389655 path_max 255 name_max 100 media sequence_number 1 vendor tag Oetiker title "RRDtool" end product tag RRDtool data_model_revision 2.40 instance_id 1 control_directory RRDtool revision 1.0.45.1 title "RRDtool (Round Robin Database) by Tobi Oetiker" mod_time 1061389655 create_time 1061389655 architecture HP-UX_B.11.00_32/64 machine_type * os_name HP-UX os_release ?.11.* os_version ? vendor_tag Oetiker directory / all_filesets RRDtool-DOC RRDtool-RUN is_locatable false fileset tag RRDtool-DOC data_model_revision 2.40 instance_id 1 control_directory RRDtool-DOC size 1144980 revision 1.0.45.0 title "RRDtool documentation" mod_time 1061389655 create_time 1061389655 state available fileset tag RRDtool-RUN data_model_revision 2.40 instance_id 1 control_directory RRDtool-RUN size 6932818 revision 1.0.45.0 title "RRDtool binary" mod_time 1061389655 create_time 1061389655 state available catalog/dfiles/INDEX010064400000000000003000000003010772070252700143670ustar00rootsys00000000000000distribution layout_version 1.0 data_model_revision 2.40 uuid 717cb270-d31a-11d7-a58a-00306e49df22 mod_time 1061389655 create_time 1061389655 path_max 255 name_max 100 media sequence_number 1 catalog/dfiles/INFO010064400000000000003000000002120772070252700142540ustar00rootsys00000000000000control_file path INDEX size 193 mode 0444 mtime 1061389655 tag INDEX control_file path INFO size 198 mode 0444 mtime 1061389655 tag INFO catalog/RRDtool/pfiles/INDEX010044400000000000003000000006450772070252700157410ustar00rootsys00000000000000vendor tag Oetiker title "RRDtool" end product tag RRDtool data_model_revision 2.40 instance_id 1 control_directory RRDtool revision 1.0.45.1 title "RRDtool (Round Robin Database) by Tobi Oetiker" mod_time 1061389655 create_time 1061389655 architecture HP-UX_B.11.00_32/64 machine_type * os_name HP-UX os_release ?.11.* os_version ? vendor_tag Oetiker directory / all_filesets RRDtool-DOC RRDtool-RUN is_locatable false catalog/RRDtool/pfiles/INFO010044400000000000003000000002120772070252700156130ustar00rootsys00000000000000control_file path INDEX size 421 mode 0444 mtime 1061389655 tag INDEX control_file path INFO size 198 mode 0444 mtime 1061389655 tag INFO catalog/RRDtool/RRDtool-DOC/INDEX010044400000000000003000000003250772070252700164020ustar00rootsys00000000000000fileset tag RRDtool-DOC data_model_revision 2.40 instance_id 1 control_directory RRDtool-DOC size 1144980 revision 1.0.45.0 title "RRDtool documentation" mod_time 1061389655 create_time 1061389655 state available catalog/RRDtool/RRDtool-DOC/INFO010044400000000000003000000275050772070252700162770ustar00rootsys00000000000000control_file path INDEX size 213 mode 0444 mtime 1061389655 tag INDEX control_file path INFO size 12159 mode 0444 mtime 1061389655 tag INFO file path /opt/rrd/doc type d mode 0755 uid 2 gid 2 owner bin group bin file path /opt/rrd/doc/RRDp.txt type f size 5411 cksum 1702560915 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/doc/RRDs.txt type f size 5646 cksum 3167178098 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/doc/bin_dec_hex.pod type f size 12861 cksum 2955497794 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/bin_dec_hex.txt type f size 19789 cksum 1903913068 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/doc/cdeftutorial.pod type f size 31767 cksum 1915688072 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/cdeftutorial.txt type f size 49516 cksum 2000383595 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rpntutorial.pod type f size 6833 cksum 3390625986 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rpntutorial.txt type f size 10509 cksum 850235919 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/doc/rrd-beginners.pod type f size 14788 cksum 2055912946 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrd-beginners.txt type f size 22906 cksum 1856618659 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/doc/rrdcgi.pod type f size 5947 cksum 2889994472 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrdcgi.txt type f size 10515 cksum 1949724825 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/doc/rrdcreate.pod type f size 9857 cksum 3050976255 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrdcreate.txt type f size 16834 cksum 3922308038 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrddump.pod type f size 689 cksum 21403429 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrddump.txt type f size 1607 cksum 1092455278 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/doc/rrdfetch.pod type f size 9625 cksum 722262682 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrdfetch.txt type f size 16784 cksum 3401833675 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/doc/rrdgraph.pod type f size 25296 cksum 2619257288 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrdgraph.txt type f size 44262 cksum 1767808241 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/doc/rrdinfo.pod type f size 1518 cksum 697079734 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrdinfo.txt type f size 2610 cksum 2678068539 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/doc/rrdlast.pod type f size 439 cksum 2950518028 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrdlast.txt type f size 1293 cksum 278935203 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrdresize.pod type f size 1331 cksum 1426069360 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrdresize.txt type f size 2584 cksum 3320379251 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/doc/rrdrestore.pod type f size 722 cksum 3473022766 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrdrestore.txt type f size 1931 cksum 2486906748 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/doc/rrdtool.pod type f size 7666 cksum 1141887125 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrdtool.txt type f size 12990 cksum 1191670859 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrdtune.pod type f size 2139 cksum 86386760 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrdtune.txt type f size 4911 cksum 3376334680 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/doc/rrdtutorial.es.pod type f size 51447 cksum 2370792157 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrdtutorial.es.txt type f size 75915 cksum 1599691828 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrdtutorial.pod type f size 48630 cksum 130096120 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrdtutorial.txt type f size 70965 cksum 3572812898 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/doc/rrdupdate.pod type f size 2610 cksum 3065361946 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrdupdate.txt type f size 4982 cksum 4281143016 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrdxport.pod type f size 4514 cksum 3016094242 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/doc/rrdxport.txt type f size 8174 cksum 1516312693 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/examples type d mode 0755 uid 2 gid 2 owner bin group bin file path /opt/rrd/examples/bigtops.pl type f size 1299 cksum 1741946995 mode 0755 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/examples/cgi-demo.cgi type f size 862 cksum 993824337 mode 0755 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/examples/minmax.pl type f size 1291 cksum 768408545 mode 0755 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/examples/piped-demo.pl type f size 4381 cksum 2515260038 mode 0755 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/examples/shared-demo.pl type f size 5779 cksum 84533390 mode 0755 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/examples/stripes.pl type f size 1454 cksum 609077638 mode 0755 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/html type d mode 0755 uid 2 gid 2 owner bin group bin file path /opt/rrd/html/RRDp.html type f size 4370 cksum 2108612798 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/html/RRDs.html type f size 4541 cksum 1165898688 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/html/bin_dec_hex.html type f size 14122 cksum 774098699 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/html/cdeftutorial.html type f size 37938 cksum 798866709 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/html/rpntutorial.html type f size 8467 cksum 3407024491 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/html/rrd-beginners.html type f size 17350 cksum 1968503704 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/html/rrdcgi.html type f size 9306 cksum 128376198 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/html/rrdcreate.html type f size 13307 cksum 1421340772 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/html/rrddump.html type f size 1646 cksum 4253803486 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/html/rrdfetch.html type f size 13196 cksum 2777651665 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/html/rrdgraph.html type f size 36440 cksum 737255804 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/html/rrdinfo.html type f size 2686 cksum 1036094421 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/html/rrdlast.html type f size 1365 cksum 3722456823 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/html/rrdresize.html type f size 2754 cksum 2870556823 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/html/rrdrestore.html type f size 1922 cksum 3982010049 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/html/rrdtool.html type f size 11610 cksum 2323315945 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/html/rrdtune.html type f size 4205 cksum 532741475 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/html/rrdtutorial.es.html type f size 56999 cksum 55129318 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/html/rrdtutorial.html type f size 54293 cksum 3453120767 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/html/rrdupdate.html type f size 4308 cksum 585758518 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/html/rrdxport.html type f size 7841 cksum 3966484031 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/man type d mode 0755 uid 2 gid 2 owner bin group bin file path /opt/rrd/man/man1.Z type d mode 0755 uid 2 gid 2 owner bin group bin file path /opt/rrd/man/man1.Z/RRDp.1 type f size 3842 cksum 181620581 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/man/man1.Z/RRDs.1 type f size 4174 cksum 1675141590 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/man/man1.Z/bin_dec_hex.1 type f size 9016 cksum 1771672723 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/man/man1.Z/cdeftutorial.1 type f size 18903 cksum 3922519518 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/man/man1.Z/rpntutorial.1 type f size 6159 cksum 1373816861 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/man/man1.Z/rrd-beginners.1 type f size 10626 cksum 2366968334 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/man/man1.Z/rrdcgi.1 type f size 6164 cksum 1853308455 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/man/man1.Z/rrdcreate.1 type f size 8107 cksum 4146255353 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/man/man1.Z/rrddump.1 type f size 2674 cksum 722063639 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/man/man1.Z/rrdfetch.1 type f size 8348 cksum 3666519717 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/man/man1.Z/rrdgraph.1 type f size 16850 cksum 2049092691 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/man/man1.Z/rrdinfo.1 type f size 3277 cksum 2544645787 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/man/man1.Z/rrdlast.1 type f size 2510 cksum 2570207116 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/man/man1.Z/rrdresize.1 type f size 3143 cksum 2569059416 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/man/man1.Z/rrdrestore.1 type f size 2744 cksum 34003889 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/man/man1.Z/rrdtool.1 type f size 6772 cksum 1149283572 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/man/man1.Z/rrdtune.1 type f size 3692 cksum 315230508 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/man/man1.Z/rrdtutorial.1 type f size 26220 cksum 2948982358 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/man/man1.Z/rrdtutorial.es.1 type f size 27872 cksum 2490266802 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388126 file path /opt/rrd/man/man1.Z/rrdupdate.1 type f size 3937 cksum 225516263 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388125 file path /opt/rrd/man/man1.Z/rrdxport.1 type f size 5033 cksum 2814261982 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388126 catalog/RRDtool/RRDtool-RUN/INDEX010044400000000000003000000003160772070252700164410ustar00rootsys00000000000000fileset tag RRDtool-RUN data_model_revision 2.40 instance_id 1 control_directory RRDtool-RUN size 6932818 revision 1.0.45.0 title "RRDtool binary" mod_time 1061389655 create_time 1061389655 state available catalog/RRDtool/RRDtool-RUN/INFO010044400000000000003000000056210772070252700163310ustar00rootsys00000000000000control_file path INDEX size 206 mode 0444 mtime 1061389655 tag INDEX control_file path INFO size 3020 mode 0444 mtime 1061389655 tag INFO file path /opt/rrd/bin type d mode 0755 uid 2 gid 2 owner bin group bin file path /opt/rrd/bin/rrdcgi type f size 72140 cksum 2516385340 mode 0755 uid 2 gid 2 owner bin group bin mtime 1061388123 file path /opt/rrd/bin/rrdtool type f size 74144 cksum 980069533 mode 0755 uid 2 gid 2 owner bin group bin mtime 1061388123 file path /opt/rrd/bin/rrdupdate type f size 60272 cksum 3575598135 mode 0755 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/bin/trytime type f size 51132 cksum 2378389210 mode 0755 uid 2 gid 2 owner bin group bin mtime 1061388127 file path /opt/rrd/contrib type d mode 0755 uid 2 gid 2 owner bin group bin file path /opt/rrd/contrib/README type f size 148 cksum 1237425172 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388127 file path /opt/rrd/contrib/trytime type d mode 0755 uid 2 gid 2 owner bin group bin file path /opt/rrd/contrib/trytime/README type f size 3717 cksum 2618116913 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388127 file path /opt/rrd/contrib/trytime/trytime.c type f size 2752 cksum 1769555991 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388127 file path /opt/rrd/include type d mode 0755 uid 2 gid 2 owner bin group bin file path /opt/rrd/include/rrd.h type f size 2019 cksum 3324743552 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388124 file path /opt/rrd/lib type d mode 0755 uid 2 gid 2 owner bin group bin file path /opt/rrd/lib/librrd.a type f size 2425304 cksum 4083664393 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388122 file path /opt/rrd/lib/librrd.la type f size 652 cksum 2118240399 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388122 file path /opt/rrd/lib/librrd.sl type s size 13 mode 0755 uid 0 gid 3 owner root group sys mtime 1061388140 link_source librrd.sl.0.0 file path /opt/rrd/lib/librrd.sl.0 type s size 13 mode 0755 uid 0 gid 3 owner root group sys mtime 1061388140 link_source librrd.sl.0.0 file path /opt/rrd/lib/librrd.sl.0.0 type f size 2387444 cksum 812031602 mode 0555 uid 2 gid 2 owner bin group bin mtime 1061388122 file path /opt/rrd/lib/perl type d mode 0755 uid 2 gid 2 owner bin group bin file path /opt/rrd/lib/perl/RRDp.pm type f size 4514 cksum 277479171 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388127 file path /opt/rrd/lib/perl/RRDs.pm type f size 3072 cksum 387869944 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388127 file path /opt/rrd/lib/perl/auto type d mode 0755 uid 2 gid 2 owner bin group bin file path /opt/rrd/lib/perl/auto/RRDs type d mode 0755 uid 2 gid 2 owner bin group bin file path /opt/rrd/lib/perl/auto/RRDs/RRDs.bs type f size 0 mode 0644 uid 2 gid 2 owner bin group bin mtime 1061388127 file path /opt/rrd/lib/perl/auto/RRDs/RRDs.sl type f size 1842256 cksum 436616335 mode 0755 uid 2 gid 2 owner bin group bin mtime 1061388127 RRDtool/RRDtool-DOC/opt/rrd/doc/004075500000020000002000000000003777777777700161235ustar00binbin00000000000000RRDtool/RRDtool-DOC/opt/rrd/doc/RRDp.txt010064400000020000002000000124430772067753500174440ustar00binbin00000000000000 RRRRRRRRDDDDpppp((((3333)))) 1111....0000....44445555 RRRRRRRRDDDDpppp((((3333)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000003333----00007777----22221111 NNNNAAAAMMMMEEEE RRDp - Attach rrdtool from within a perl script via a set of pipes; SSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS use RRRRRRRRDDDDpppp RRRRRRRRDDDDpppp::::::::ssssttttaaaarrrrtttt _p_a_t_h _t_o _r_r_d_t_o_o_l _e_x_e_c_u_t_a_b_l_e RRRRRRRRDDDDpppp::::::::ccccmmmmdddd _r_r_d_t_o_o_l _c_o_m_m_a_n_d_l_i_n_e $answer = RRRRRRRRDDDD::::::::rrrreeeeaaaadddd $status = RRRRRRRRDDDD::::::::eeeennnndddd $$$$RRRRRRRRDDDDpppp::::::::uuuusssseeeerrrr, $$$$RRRRRRRRDDDDpppp::::::::ssssyyyyssss, $$$$RRRRRRRRDDDDpppp::::::::rrrreeeeaaaallll DDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN With this module you can safely communicate with the rrdtool. After every RRRRRRRRDDDDpppp::::::::ccccmmmmdddd you have to issue an RRRRRRRRDDDDpppp::::::::rrrreeeeaaaadddd command to get rrrrrrrrddddttttoooooooolllls answer to your command. The answer is returned as a pointer, in order to speed things up. If the last command did not return any data, RRRRRRRRDDDDpppp::::::::rrrreeeeaaaadddd will return an undefined variable. If you import the PERFORMANCE variables into your namespace, you can access rrdtools internal performance measurements. use RRRRRRRRDDDDpppp Load the RRDp::pipe module. RRRRRRRRDDDDpppp::::::::ssssttttaaaarrrrtttt _p_a_t_h _t_o _r_r_d_t_o_o_l _e_x_e_c_u_t_a_b_l_e start rrdtool. The argument must be the path to the rrdtool executable RRRRRRRRDDDDpppp::::::::ccccmmmmdddd _r_r_d_t_o_o_l _c_o_m_m_a_n_d_l_i_n_e pass commands on to rrdtool. check the rrdtool documentation for more info on the rrdtool commands. $answer = RRRRRRRRDDDDpppp::::::::rrrreeeeaaaadddd read rrdtools response to your command. Note that the $answer variable will only contain a pointer to the returned data. The reason for this is, that rrdtool can potentially return quite excessive amounts of data and we don't want to copy this around in memory. So when you want to access the contents of $answer you have to use $$answer which dereferences the variable. $status = RRRRRRRRDDDDpppp::::::::eeeennnndddd terminates rrdtool and returns rrdtools status ... - 1 - Formatted: August 20, 2003 RRRRRRRRDDDDpppp((((3333)))) 1111....0000....44445555 RRRRRRRRDDDDpppp((((3333)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000003333----00007777----22221111 $$$$RRRRRRRRDDDDpppp::::::::uuuusssseeeerrrr, $$$$RRRRRRRRDDDDpppp::::::::ssssyyyyssss, $$$$RRRRRRRRDDDDpppp::::::::rrrreeeeaaaallll these variables will contain totals of the user time, system time and real time as seen by rrdtool. User time is the time rrdtool is running, System time is the time spend in system calls and real time is the total time rrdtool has been running. The difference between user + system and real is the time spent waiting for things like the hard disk and new input from the perl script. EEEEXXXXAAAAMMMMPPPPLLLLEEEE use RRDp; RRDp::start "/usr/local/bin/rrdtool"; RRDp::cmd qw(create demo.rrd --step 100 DS:in:GAUGE:100:U:U RRA:AVERAGE:0.5:1:10); $answer = RRDp::read; print $$answer; ($usertime,$systemtime,$realtime) = ($RRDp::user,$RRDp::sys,$RRDp::real); SSSSEEEEEEEE AAAALLLLSSSSOOOO For more information on how to use rrdtool, check the manpages. AAAAUUUUTTTTHHHHOOOORRRR Tobias Oetiker - 2 - Formatted: August 20, 2003 RRDtool/RRDtool-DOC/opt/rrd/doc/RRDs.txt010064400000020000002000000130160772067753500174440ustar00binbin00000000000000 RRRRRRRRDDDDssss((((3333)))) 1111....0000....44445555 RRRRRRRRDDDDssss((((3333)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000003333----00007777----22221111 NNNNAAAAMMMMEEEE RRDs - Access rrdtool as a shared module SSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS use RRDs; RRDs::error RRDs::last ... RRDs::info ... RRDs::create ... RRDs::update ... RRDs::graph ... RRDs::fetch ... RRDs::tune ... DDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN CCCCaaaalllllllliiiinnnngggg SSSSeeeeqqqquuuueeeennnncccceeee This module accesses rrdtool functionality directly from within perl. The arguments to the functions listed in the SYNOPSIS are explained in the regular rrdtool documentation. The commandline call rrdtool update mydemo.rrd --template in:out N:12:13 gets turned into RRDs::update ("mydemo.rrd", "--template", "in:out", "N:12:13"); Note that --template=in:out is also valid. EEEErrrrrrrroooorrrr HHHHaaaannnnddddlllliiiinnnngggg The RRD functions will not abort your program even when they can not make sense out of the arguments you fed them. The function RRDs::error should be called to get the error status after each function call. If RRDs::error does not return anything then the previous function has completed its task successfully. use RRDs; RRDs::update ("mydemo.rrd","N:12:13"); my $ERR=RRDs::error; die "ERROR while updating mydemo.rrd: $ERR\n" if $ERR; - 1 - Formatted: August 20, 2003 RRRRRRRRDDDDssss((((3333)))) 1111....0000....44445555 RRRRRRRRDDDDssss((((3333)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000003333----00007777----22221111 RRRReeeettttuuuurrrrnnnn VVVVaaaalllluuuueeeessss The functions RRDs::last, RRDs::graph, RRDs::info and RRDs::fetch return their findings. RRRRRRRRDDDDssss::::::::llllaaaasssstttt returns a single INTEGER representing the last update time. $lastupdate = RRDs::last ... RRRRRRRRDDDDssss::::::::ggggrrrraaaapppphhhh returns an pointer to an ARRAY containing the x-size and y-size of the created gif and results of the PRINT arguments. ($averages,$xsize,$ysize) = RRDs::graph ... print "Gifsize: ${xsize}x${ysize}\n"; print "Averages: ", (join ", ", @$averages); RRRRRRRRDDDDssss::::::::iiiinnnnffffoooo returns a pointer to a hash. The keys of the hash represent the property names of the rrd and the values of the hash are the values of the properties. $hash = RRDs::info "example.rrd"; foreach my $key (keys %$hash){ print "$key = $$hash{$key}\n"; } RRRRRRRRDDDDssss::::::::ffffeeeettttcccchhhh is the most complex of the pack regarding return values. There are 4 values. Two normal integers, a pointer to an array and a pointer to a array of pointers. my ($start,$step,$names,$data) = RRDs::fetch ... print "Start: ", scalar localtime($start), " ($start)\n"; print "Step size: $step seconds\n"; print "DS names: ", join (", ", @$names)."\n"; print "Data points: ", $#$data + 1, "\n"; print "Data:\n"; foreach my $line (@$data) { print " ", scalar localtime($start), " ($start) "; $start += $step; foreach my $val (@$line) { printf "%12.1f ", $val; } print "\n"; } See the examples directory for more ways to use this extension. NNNNOOOOTTTTEEEE If you are manipulating the TZ variable you should also call the posixs function tzset to initialize all internal state of the library for properly operating in the timezone of your choice. - 2 - Formatted: August 20, 2003 RRRRRRRRDDDDssss((((3333)))) 1111....0000....44445555 RRRRRRRRDDDDssss((((3333)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000003333----00007777----22221111 use POSIX qw(tzset); $ENV{TZ} = 'CET'; POSIX::tzset(); AAAAUUUUTTTTHHHHOOOORRRR Tobias Oetiker - 3 - Formatted: August 20, 2003 RRDtool/RRDtool-DOC/opt/rrd/doc/bin_dec_hex.pod010064400000020000002000000310750772067753400210300ustar00binbin00000000000000=head1 NAME Binary Decimal Hexadecimal - How does it work =for html
PDF version.
=head1 DESCRIPTION Most people use the decimal numbering system. This system uses ten symbols to represent numbers. When those ten symbols are used up, they start all over again and increment the position just before this. The digit 0 is only shown if it is the only symbol in the sequence, or if it is not the first one. If this sounds as crypto to you, this is what I've said in numbers: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 and so on. Each time the digit nine should be incremented, it is reset to 0 and the position before is incremented. Then number 9 can be seen as "00009" and when we should increment 9, we reset it to zero and increment the digit just before the 9 so the number becomes "00010". For zero's we write a space if it is not the only digit (so: number 0) and if it is the first digit: "00010" -> " 0010" -> " 010" -> " 10". It is not " 1 ". This was pretty basic, you already knew this. Why did I tell it ? Well, computers do not represent numbers with 10 different digits. They know of only two different symbols, being 0 and 1. Apply the same rules to this set of digits and you get the binary numbering system: 0 1 10 11 100 101 110 111 1000 1001 1010 1011 1100 1101 and so on. If you count the number of rows, you'll see that these are again 14 different numbers. The numbers are the same and mean the same. It is only a different representation. This means that you have to know the representation used, or as it is called the numbering system or base. Normally if we do not speak about the numbering system used, we're using the decimal system. If we are talking about another numbering system, we'll have to make that clear. There are a few wide-spread methods to do so. One common form is to write 1010(2) which means that you wrote down a number in the binary form. It is the number ten. If you would write 1010 it means the number one thousand and ten. In books, another form is most used. It uses subscript (little chars, more or less in between two rows). You can leave out the parentheses in that case and write down the number in normal characters followed with a little two just behind it. The numbering system used is also called the base. We talk of the number 1100 base 2, the number 12 base 10. For the binary system, is is common to write leading zero's. The numbers are written down in series of four, eight or sixteen depending on the context. We can use the binary form when talking to computers (...programming...) but the numbers will have large representations. The number 65535 would be written down as 1111111111111111(2) which is 16 times the digit 1. This is difficult and prone to errors. Therefore we normally would use another base, called hexadecimal. It uses 16 different symbols. First the symbols from the decimal system are used, thereafter we continue with the alphabetic characters. We get 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E and F. This system is chosen because the hexadecimal form can be converted into the binary system very easy (and back). There is yet another system in use, called the octal system. This was more common in the old days but not anymore. You will find it in use on some places so get used to it. The same story applies, but now with only eight different symbols. Binary (2) Octal (8) Decimal (10) Hexadecimal (16) (2) (8) (10) (16) 00000 0 0 0 00001 1 1 1 00010 2 2 2 00011 3 3 3 00100 4 4 4 00101 5 5 5 00110 6 6 6 00111 7 7 7 01000 10 8 8 01001 11 9 9 01010 12 10 A 01011 13 11 B 01100 14 12 C 01101 15 13 D 01110 16 14 E 01111 17 15 F 10000 20 16 10 10001 21 17 11 10010 22 18 12 10011 23 19 13 10100 24 20 14 10101 25 21 15 Most computers used nowadays are using bytes of eight bits. This means that they store eight bits at a time. You can see why the octal system is not the most preferred for that: You'd need three digits to represent the eight bits and this means that you'd have to use one complete digit to represent only two bits (2+3+3=8). This is a waste. For hexadecimal digits, you need only two digits which are used completely: (2) (8) (10) (16) 11111111 377 255 FF You can see why binary and hexadecimal can be converted quickly: For each hexadecimal digit there are exactly four binary digits. Take a binary number. Each time take four digits from the right and make a hexadecimal digit from it (see the table above). Stop when there are no more digits. Other way around: Take a hexadecimal number. For each digit, write down its binary equivalent. Computers (or rather the parsers running on them) would have a hard time converting a number like 1234(16). Therefore hexadecimal numbers get a prefix. This prefix depends on the language you're writing in. Some of the prefixes are "0x" for C, "$" for Pascal, "#" for HTML. It is common to assume that if a number starts with a zero, it is octal. It does not matter what is used as long as you know what it is. I will use "0x" for hexadecimal, "%" for binary and "0" for octal. The following numbers are all the same, just the way they are written is different: 021 0x11 17 %00010001 To do arithmetics and conversions you need to understand one more thing. It is something you already know but perhaps you do not "see" it yet: If you write down 1234, (so it is decimal) you are talking about the number one thousand, two hundred and thirty four. In sort of a formula: 1 * 1000 = 1000 2 * 100 = 200 3 * 10 = 30 4 * 1 = 4 This can also be written as: 1 * 10^3 2 * 10^2 3 * 10^1 4 * 10^0 where ^ means "to the power of". We are using the base 10, and the positions 0,1,2 and 3. The right-most position should NOT be multiplied with 10. The second from the right should be multiplied one time with 10. The third from the right is multiplied with 10 two times. This continues for whatever positions are used. It is the same in all other representations: 0x1234 will be 1 * 16^3 2 * 16^2 3 * 16^1 4 * 16^0 01234 would be 1 * 8^3 2 * 8^2 3 * 8^1 4 * 8^0 This example can not be done for binary as that system can only use two symbols. Another example: %1010 would be 1 * 2^3 0 * 2^2 1 * 2^1 0 * 2^0 It would have been more easy to convert it to its hexadecimal form and just translate %1010 into 0xA. After a while you get used to it. You will not need to do any calculations anymore but just know that 0xA means 10. To convert a decimal number into a hexadecimal one you could use the next method. It will take some time to be able to do the estimates but it will be more and more easy when you use the system more frequent. Another way is presented to you thereafter. First you will need to know how many positions will be used in the other system. To do so, you need to know the maximum numbers. Well, that's not so hard as it looks. In decimal, the maximum number that you can form with two digits is "99". The maximum for three: "999". The next number would need an extra position. Reverse this idea and you will see that the number can be found by taking 10^3 (10*10*10 is 1000) minus 1 or 10^2 minus one. This can be done for hexadecimal too: 16^4 = 0x10000 = 65536 16^3 = 0x1000 = 4096 16^2 = 0x100 = 256 16^1 = 0x10 = 16 If a number is smaller than 65536 it will thus fit in four positions. If the number is bigger than 4095, you will need to use position 4. How many times can you take 4096 from the number without going below zero is the first digit you write down. This will always be a number from 1 to 15 (0x1 to 0xF). Do the same for the other positions. Number is 41029. It is smaller than 16^4 but bigger than 16^3-1. This means that we have to use four positions. We can subtract 16^3 from 41029 ten times without going below zero. The leftmost digit will be "A" so we have 0xA????. The number is reduced to 41029 - 10*4096 = 41029-40960 = 69. 69 is smaller than 16^3 but not bigger than 16^2-1. The second digit is therefore "0" and we know 0xA0??. 69 is smaller than 16^2 and bigger than 16^1-1. We can subtract 16^1 (which is just plain 16) four times and write down "4" to get 0xA04?. Take 64 from 69 (69 - 4*16) and the last digit is 5 --> 0xA045. The other method builds the number from the right. Take again 41029. Divide by 16 and do not use fractions (only whole numbers). 41029 / 16 is 2564 with a remainder of 5. Write down 5. 2564 / 16 is 160 with a remainder of 4. Write the 4 before the 5. 160 / 16 is 10 with no remainder. Prepend 45 with 0. 10 / 16 is below one. End here and prepend 0xA. End up with 0xA045. Which method to use is up to you. Use whatever works for you. Personally I use them both without being able to tell what method I use in each case, it just depends on the number, I think. Fact is, some numbers will occur frequently while programming, if the number is close then I will use the first method (like 32770, translate into 32768 + 2 and just know that it is 0x8000 + 0x2 = 0x8002). For binary the same approach can be used. The base is 2 and not 16, and the number of positions will grow rapidly. Using the second method has the advantage that you can see very simple if you should write down a zero or a one: if you divide by two the remainder will be zero if it was an even number and one if it was an odd number: 41029 / 2 = 20514 remainder 1 20514 / 2 = 10257 remainder 0 10257 / 2 = 5128 remainder 1 5128 / 2 = 2564 remainder 0 2564 / 2 = 1282 remainder 0 1282 / 2 = 641 remainder 0 641 / 2 = 320 remainder 1 320 / 2 = 160 remainder 0 160 / 2 = 80 remainder 0 80 / 2 = 40 remainder 0 40 / 2 = 20 remainder 0 20 / 2 = 10 remainder 0 10 / 2 = 5 remainder 0 5 / 2 = 2 remainder 1 2 / 2 = 1 remainder 0 1 / 2 below 0 remainder 1 Write down the results from right to left: %1010000001000101 Group by four: %1010000001000101 %101000000100 0101 %10100000 0100 0101 %1010 0000 0100 0101 Convert into hexadecimal: 0xA045 Group %1010000001000101 by three and convert into octal: %1010000001000101 %1010000001000 101 %1010000001 000 101 %1010000 001 000 101 %1010 000 001 000 101 %1 010 000 001 000 101 %001 010 000 001 000 101 1 2 0 1 0 5 --> 0120105 So: %1010000001000101 = 0120105 = 0xA045 = 41029 Or: 1010000001000101(2) = 120105(8) = A045(16) = 41029(10) Or: 1010000001000101(2) = 120105(8) = A045(16) = 41029 At first while adding numbers, you'll convert them to their decimal form and then back into their original form after doing the addition. If you use the other numbering system often, you will see that you'll be able to do arithmetics in the base that is used. In any representation it is the same, add the numbers on the right, write down the rightmost digit from the result, remember the other digits and use them in the next round. Continue with the second digits from the right and so on: %1010 + %0111 --> 10 + 7 --> 17 --> %00010001 will become %1010 %0111 + |||| |||+-- add 0 + 1, result is 1, nothing to remember ||+--- add 1 + 1, result is %10, write down 0 and remember 1 |+---- add 0 + 1 + 1(remembered), result = 0, remember 1 +----- add 1 + 0 + 1(remembered), result = 0, remember 1 nothing to add, 1 remembered, result = 1 -------- %10001 is the result, I like to write it as %00010001 For low values, try to do the calculations yourself, check them with a calculator. The more you do the calculations yourself, the more you find that you didn't make mistakes. In the end, you'll do calculi in other bases as easy as you do in decimal. When the numbers get bigger, you'll have to realize that a computer is not called a computer just to have a nice name. There are many different calculators available. Use them. For Unix you could use "bc" which is called so as it is short for Binary Calculator. It calculates not only in decimal, but in all bases you'll ever use (among them Binary). For people on Windows: Start the calculator (start->programs->accessories->calculator) and if necessary click view->scientific. You now have a scientific calculator and can compute in binary or hexadecimal. =head1 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 answer but at the same time learn a whole lot more. Alex van den Bogaerdt RRDtool/RRDtool-DOC/opt/rrd/doc/bin_dec_hex.txt010064400000020000002000000465150772067753500210730ustar00binbin00000000000000 BBBBIIIINNNN____DDDDEEEECCCC____HHHHEEEEXXXX((((1111)))) 1111....0000....44445555 BBBBIIIINNNN____DDDDEEEECCCC____HHHHEEEEXXXX((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00002222----22226666 NNNNAAAAMMMMEEEE Binary Decimal Hexadecimal - How does it work DDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN Most people use the decimal numbering system. This system uses ten symbols to represent numbers. When those ten symbols are used up, they start all over again and increment the position just before this. The digit 0 is only shown if it is the only symbol in the sequence, or if it is not the first one. If this sounds as crypto to you, this is what I've said in numbers: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 and so on. Each time the digit nine should be incremented, it is reset to 0 and the position before is incremented. Then number 9 can be seen as "00009" and when we should increment 9, we reset it to zero and increment the digit just before the 9 so the number becomes "00010". For zero's we write a space if it is not the only digit (so: number 0) and if it is the first digit: "00010" -> " 0010" -> " 010" -> " 10". It is not " 1 ". This was pretty basic, you already knew this. Why did I tell it ? Well, computers do not represent numbers with 10 different digits. They know of only two different symbols, being 0 and 1. Apply the same rules to this set of digits and you get the binary numbering system: - 1 - Formatted: August 20, 2003 BBBBIIIINNNN____DDDDEEEECCCC____HHHHEEEEXXXX((((1111)))) 1111....0000....44445555 BBBBIIIINNNN____DDDDEEEECCCC____HHHHEEEEXXXX((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00002222----22226666 0 1 10 11 100 101 110 111 1000 1001 1010 1011 1100 1101 and so on. If you count the number of rows, you'll see that these are again 14 different numbers. The numbers are the same and mean the same. It is only a different representation. This means that you have to know the representation used, or as it is called the numbering system or base. Normally if we do not speak about the numbering system used, we're using the decimal system. If we are talking about another numbering system, we'll have to make that clear. There are a few wide-spread methods to do so. One common form is to write 1010(2) which means that you wrote down a number in the binary form. It is the number ten. If you would write 1010 it means the number one thousand and ten. In books, another form is most used. It uses subscript (little chars, more or less in between two rows). You can leave out the parentheses in that case and write down the number in normal characters followed with a little two just behind it. The numbering system used is also called the base. We talk of the number 1100 base 2, the number 12 base 10. For the binary system, is is common to write leading zero's. The numbers are written down in series of four, eight or sixteen depending on the context. We can use the binary form when talking to computers (...programming...) but the numbers will have large representations. The number 65535 would be written down as 1111111111111111(2) which is 16 times the digit 1. This is difficult and prone to errors. Therefore we normally would use another base, called hexadecimal. It uses 16 different symbols. First the symbols from the decimal system are used, thereafter we continue with the alphabetic characters. We get 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E and F. This system is chosen because the hexadecimal form can be converted into the binary system very easy (and back). - 2 - Formatted: August 20, 2003 BBBBIIIINNNN____DDDDEEEECCCC____HHHHEEEEXXXX((((1111)))) 1111....0000....44445555 BBBBIIIINNNN____DDDDEEEECCCC____HHHHEEEEXXXX((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00002222----22226666 There is yet another system in use, called the octal system. This was more common in the old days but not anymore. You will find it in use on some places so get used to it. The same story applies, but now with only eight different symbols. Binary (2) Octal (8) Decimal (10) Hexadecimal (16) (2) (8) (10) (16) 00000 0 0 0 00001 1 1 1 00010 2 2 2 00011 3 3 3 00100 4 4 4 00101 5 5 5 00110 6 6 6 00111 7 7 7 01000 10 8 8 01001 11 9 9 01010 12 10 A 01011 13 11 B 01100 14 12 C 01101 15 13 D 01110 16 14 E 01111 17 15 F 10000 20 16 10 10001 21 17 11 10010 22 18 12 10011 23 19 13 10100 24 20 14 10101 25 21 15 Most computers used nowadays are using bytes of eight bits. This means that they store eight bits at a time. You can see why the octal system is not the most preferred for that: You'd need three digits to represent the eight bits and this means that you'd have to use one complete digit to represent only two bits (2+3+3=8). This is a waste. For hexadecimal digits, you need only two digits which are used completely: (2) (8) (10) (16) 11111111 377 255 FF You can see why binary and hexadecimal can be converted quickly: For each hexadecimal digit there are exactly four binary digits. Take a binary number. Each time take four digits from the right and make a hexadecimal digit from it (see the table above). Stop when there are no more digits. Other way around: Take a hexadecimal number. For each - 3 - Formatted: August 20, 2003 BBBBIIIINNNN____DDDDEEEECCCC____HHHHEEEEXXXX((((1111)))) 1111....0000....44445555 BBBBIIIINNNN____DDDDEEEECCCC____HHHHEEEEXXXX((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00002222----22226666 digit, write down its binary equivalent. Computers (or rather the parsers running on them) would have a hard time converting a number like 1234(16). Therefore hexadecimal numbers get a prefix. This prefix depends on the language you're writing in. Some of the prefixes are "0x" for C, "$" for Pascal, "#" for HTML. It is common to assume that if a number starts with a zero, it is octal. It does not matter what is used as long as you know what it is. I will use "0x" for hexadecimal, "%" for binary and "0" for octal. The following numbers are all the same, just the way they are written is different: 021 0x11 17 %00010001 To do arithmetics and conversions you need to understand one more thing. It is something you already know but perhaps you do not "see" it yet: If you write down 1234, (so it is decimal) you are talking about the number one thousand, two hundred and thirty four. In sort of a formula: 1 * 1000 = 1000 2 * 100 = 200 3 * 10 = 30 4 * 1 = 4 This can also be written as: 1 * 10^3 2 * 10^2 3 * 10^1 4 * 10^0 where ^ means "to the power of". We are using the base 10, and the positions 0,1,2 and 3. The right- most position should NOT be multiplied with 10. The second from the right should be multiplied one time with 10. The third from the right is multiplied with 10 two times. This continues for whatever positions are used. It is the same in all other representations: 0x1234 will be 1 * 16^3 2 * 16^2 3 * 16^1 4 * 16^0 01234 would be - 4 - Formatted: August 20, 2003 BBBBIIIINNNN____DDDDEEEECCCC____HHHHEEEEXXXX((((1111)))) 1111....0000....44445555 BBBBIIIINNNN____DDDDEEEECCCC____HHHHEEEEXXXX((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00002222----22226666 1 * 8^3 2 * 8^2 3 * 8^1 4 * 8^0 This example can not be done for binary as that system can only use two symbols. Another example: %1010 would be 1 * 2^3 0 * 2^2 1 * 2^1 0 * 2^0 It would have been more easy to convert it to its hexadecimal form and just translate %1010 into 0xA. After a while you get used to it. You will not need to do any calculations anymore but just know that 0xA means 10. To convert a decimal number into a hexadecimal one you could use the next method. It will take some time to be able to do the estimates but it will be more and more easy when you use the system more frequent. Another way is presented to you thereafter. First you will need to know how many positions will be used in the other system. To do so, you need to know the maximum numbers. Well, that's not so hard as it looks. In decimal, the maximum number that you can form with two digits is "99". The maximum for three: "999". The next number would need an extra position. Reverse this idea and you will see that the number can be found by taking 10^3 (10*10*10 is 1000) minus 1 or 10^2 minus one. This can be done for hexadecimal too: 16^4 = 0x10000 = 65536 16^3 = 0x1000 = 4096 16^2 = 0x100 = 256 16^1 = 0x10 = 16 If a number is smaller than 65536 it will thus fit in four positions. If the number is bigger than 4095, you will need to use position 4. How many times can you take 4096 from the number without going below zero is the first digit you write down. This will always be a number from 1 to 15 (0x1 to 0xF). Do the same for the other positions. Number is 41029. It is smaller than 16^4 but bigger than 16^3-1. This means that we have to use four positions. We can subtract 16^3 from 41029 ten times without going below zero. The leftmost digit will be "A" so we have 0xA????. The number is reduced to 41029 - 10*4096 = - 5 - Formatted: August 20, 2003 BBBBIIIINNNN____DDDDEEEECCCC____HHHHEEEEXXXX((((1111)))) 1111....0000....44445555 BBBBIIIINNNN____DDDDEEEECCCC____HHHHEEEEXXXX((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00002222----22226666 41029-40960 = 69. 69 is smaller than 16^3 but not bigger than 16^2-1. The second digit is therefore "0" and we know 0xA0??. 69 is smaller than 16^2 and bigger than 16^1-1. We can subtract 16^1 (which is just plain 16) four times and write down "4" to get 0xA04?. Take 64 from 69 (69 - 4*16) and the last digit is 5 --> 0xA045. The other method builds the number from the right. Take again 41029. Divide by 16 and do not use fractions (only whole numbers). 41029 / 16 is 2564 with a remainder of 5. Write down 5. 2564 / 16 is 160 with a remainder of 4. Write the 4 before the 5. 160 / 16 is 10 with no remainder. Prepend 45 with 0. 10 / 16 is below one. End here and prepend 0xA. End up with 0xA045. Which method to use is up to you. Use whatever works for you. Personally I use them both without being able to tell what method I use in each case, it just depends on the number, I think. Fact is, some numbers will occur frequently while programming, if the number is close then I will use the first method (like 32770, translate into 32768 + 2 and just know that it is 0x8000 + 0x2 = 0x8002). For binary the same approach can be used. The base is 2 and not 16, and the number of positions will grow rapidly. Using the second method has the advantage that you can see very simple if you should write down a zero or a one: if you divide by two the remainder will be zero if it was an even number and one if it was an odd number: 41029 / 2 = 20514 remainder 1 20514 / 2 = 10257 remainder 0 10257 / 2 = 5128 remainder 1 5128 / 2 = 2564 remainder 0 2564 / 2 = 1282 remainder 0 1282 / 2 = 641 remainder 0 641 / 2 = 320 remainder 1 320 / 2 = 160 remainder 0 160 / 2 = 80 remainder 0 80 / 2 = 40 remainder 0 40 / 2 = 20 remainder 0 20 / 2 = 10 remainder 0 10 / 2 = 5 remainder 0 5 / 2 = 2 remainder 1 2 / 2 = 1 remainder 0 1 / 2 below 0 remainder 1 Write down the results from right to left: %1010000001000101 Group by four: - 6 - Formatted: August 20, 2003 BBBBIIIINNNN____DDDDEEEECCCC____HHHHEEEEXXXX((((1111)))) 1111....0000....44445555 BBBBIIIINNNN____DDDDEEEECCCC____HHHHEEEEXXXX((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00002222----22226666 %1010000001000101 %101000000100 0101 %10100000 0100 0101 %1010 0000 0100 0101 Convert into hexadecimal: 0xA045 Group %1010000001000101 by three and convert into octal: %1010000001000101 %1010000001000 101 %1010000001 000 101 %1010000 001 000 101 %1010 000 001 000 101 %1 010 000 001 000 101 %001 010 000 001 000 101 1 2 0 1 0 5 --> 0120105 So: %1010000001000101 = 0120105 = 0xA045 = 41029 Or: 1010000001000101(2) = 120105(8) = A045(16) = 41029(10) Or: 1010000001000101(2) = 120105(8) = A045(16) = 41029 At first while adding numbers, you'll convert them to their decimal form and then back into their original form after doing the addition. If you use the other numbering system often, you will see that you'll be able to do arithmetics in the base that is used. In any representation it is the same, add the numbers on the right, write down the rightmost digit from the result, remember the other digits and use them in the next round. Continue with the second digits from the right and so on: %1010 + %0111 --> 10 + 7 --> 17 --> %00010001 will become %1010 %0111 + |||| |||+-- add 0 + 1, result is 1, nothing to remember ||+--- add 1 + 1, result is %10, write down 0 and remember 1 |+---- add 0 + 1 + 1(remembered), result = 0, remember 1 +----- add 1 + 0 + 1(remembered), result = 0, remember 1 nothing to add, 1 remembered, result = 1 -------- %10001 is the result, I like to write it as %00010001 For low values, try to do the calculations yourself, check them with a calculator. The more you do the calculations yourself, the more you find that you didn't make mistakes. In the end, you'll do calculi in other bases as easy as you do in decimal. - 7 - Formatted: August 20, 2003 BBBBIIIINNNN____DDDDEEEECCCC____HHHHEEEEXXXX((((1111)))) 1111....0000....44445555 BBBBIIIINNNN____DDDDEEEECCCC____HHHHEEEEXXXX((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00002222----22226666 When the numbers get bigger, you'll have to realize that a computer is not called a computer just to have a nice name. There are many different calculators available. Use them. For Unix you could use "bc" which is called so as it is short for Binary Calculator. It calculates not only in decimal, but in all bases you'll ever use (among them Binary). For people on Windows: Start the calculator (start->programs->accessories->calculator) and if necessary click view->scientific. You now have a scientific calculator and can compute in binary or hexadecimal. AAAAUUUUTTTTHHHHOOOORRRR 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 answer but at the same time learn a whole lot more. Alex van den Bogaerdt - 8 - Formatted: August 20, 2003 RRDtool/RRDtool-DOC/opt/rrd/doc/cdeftutorial.pod010064400000020000002000000760270772067753400212740ustar00binbin00000000000000=head1 NAME cdeftutorial - Alex van den Bogaerdt's CDEF tutorial =for html
PDF version.
=head1 DESCRIPTION B. B I Alex van den Bogaerdt Ealex@ergens.op.het.netE =head2 Why this tutorial ? One of the powerful parts of RRDtool is its ability to do all sorts of calculations on the data retrieved from it's databases. However RRDtool's many options and syntax make it difficult for the average user to understand. The manuals are good at explaining what these options do; however they do not (and should not) explain in detail why they are useful. As with my RRDtool tutorial: if you want a simple document in simple language you should read this tutorial. If you are happy with the official documentation, you may find this document too simple or even boring. If you do choose to read this tutorial, I also expect you to have read and fully understand my other tutorial. =head2 More reading If you have difficulties with the way I try to explain it please read Steve Rader's L. It may help you understand how this all works. =head1 What are CDEFs ? When retrieving data from an RRD, you are using a "DEF" to work with that data. Think of it as a variable that changes over time (where time is the x-axis). The value of this variable is what is found in the database at that particular time and you can't do any modifications on it. This is what CDEFs are for: they takes values from DEFs and perform calculations on them. =head1 Syntax DEF:var_name_1=some.rrd:ds_name:CF CDEF:var_name_2=RPN_expression You first define "var_name_1" to be data collected from data source "ds_name" found in RRD "some.rrd" with consolidation function "CF". Assume the ifInOctets SNMP counter is saved in mrtg.rrd as the DS "in". Then the following DEF defines a variable for the average of that data source: DEF:inbytes=mrtg.rrd:in:AVERAGE Say you want to display bits per second (instead of bytes per second as stored in the database.) You have to define a calculation (hence "CDEF") on variable "inbytes" and use that variable (inbits) instead of the original: CDEF:inbits=inbytes,8,* It tells to multiply inbytes by eight to get inbits. I'll explain later how this works. In the graphing or printing functions, you can now use inbits where you would use inbytes otherwise. Note that variable in the CDEF (inbits) must not be the same as the variable (inbytes) in the DEF! =head1 RPN-expressions RPN is short-hand for Reverse Polish Notation. It works as follows. You put the variables or numbers on a stack. You also put operations (things-to-do) on the stack and this stack is then processed. The result will be placed on the stack. At the end, there should be exactly one number left: the outcome of the series of operations. If there is not exactly one number left, rrdtool will complain loudly. Above multiplication by eight will look like: =over 4 =item 1. Start with an empty stack =item 2. Put the content of variable inbytes on the stack =item 3. Put the number eight on the stack =item 4. Put the operation multiply on the stack =item 5. Process the stack =item 6. Retrieve the value from the stack and put it in variable inbits =back We will now do an example with real numbers. Suppose the variable inbytes would have value 10, the stack would be: =over 4 =item 1. || =item 2. |10| =item 3. |10|8| =item 4. |10|8|*| =item 5. |80| =item 6. || =back Processing the stack (step 5) will retrieve one value from the stack (from the right at step 4). This is the operation multiply and this takes two values off the stack as input. The result is put back on the stack (the value 80 in this case). For multiplication the order doesn't matter but for other operations like subtraction and division it does. Generally speaking you have the following order: y = A - B --> y=minus(A,B) --> CDEF:y=A,B,- This is not very intuitive (at least most people don't think so). For the function f(A,B) you reverse the position of "f" but you do not reverse the order of the variables. =head1 Converting your wishes to RPN First, get a clear picture of what you want to do. Break down the problem in smaller portions until they cannot be split anymore. Then it is rather simple to convert your ideas into RPN. Suppose you have several RRDs and would like to add up some counters in them. These could be, for instance, the counters for every WAN link you are monitoring. You have: router1.rrd with link1in link2in router2.rrd with link1in link2in router3.rrd with link1in link2in Suppose you would like to add up all these counters, except for link2in inside router2.rrd. You need to do: (in this example, "router1.rrd:link1in" means the DS link1in inside the RRD router1.rrd) router1.rrd:link1in router1.rrd:link2in router2.rrd:link1in router3.rrd:link1in router3.rrd:link2in -------------------- + (outcome of the sum) As a mathmatical function, this could be written: C With RRDtool and RPN, first, define the inputs: DEF:a=router1.rrd:link1in:AVERAGE DEF:b=router1.rrd:link2in:AVERAGE DEF:c=router2.rrd:link1in:AVERAGE DEF:d=router3.rrd:link1in:AVERAGE DEF:e=router3.rrd:link2in:AVERAGE Now, the mathematical function becomes: C In RPN, there's no operator that sums more than two values so you need to do several additions. You add a and b, add c to the result, add d to the result and add e to the result. push a: a stack contains the value of a push b and add: b,+ stack contains the result of a+b push c and add: c,+ stack contains the result of a+b+c push d and add: d,+ stack contains the result of a+b+c+d push e and add: e,+ stack contains the result of a+b+c+d+e What was calculated here would be written down as: ( ( ( (a+b) + c) + d) + e) > This is in RPN: C This is correct but it can be made more clear to humans. It does not matter if you add a to b and then add c to the result or first add b to c and then add a to the result. This makes it possible to rewrite the RPN into C which is evaluatated differently: push value of variable a on the stack: a push value of variable b on the stack: a b push value of variable c on the stack: a b c push value of variable d on the stack: a b c d push value of variable e on the stack: a b c d e push operator + on the stack: a b c d e + and process it: a b c P (where P == d+e) push operator + on the stack: a b c P + and process it: a b Q (where Q == c+P) push operator + on the stack: a b Q + and process it: a R (where R == b+Q) push operator + on the stack: a R + and process it: S (where S == a+R) As you can see the RPN expression C will evaluate in C<((((d+e)+c)+b)+a)> and it has the same outcome as C According to Steve Rader this is called the commutative law of addition but you may forget this right away, as long as you remember what it represents. Now look at an expression that contains a multiplication: First in normal math: C. In this case you can't choose the order yourself, you have to start with the multiplication and then add a to it. You may alter the position of b and c, you may not alter the position of a and b. You have to take this in consideration when converting this expression into RPN. Read it as: "Add the outcome of b*c to a" and then it is easy to write the RPN expression: C Another expression that would return the same: C In normal math, you may encounter something like "a*(b+c)" and this can also be converted into RPN. The parenthesis just tell you to first add b and c, and then multiply a with the result. Again, now it is easy to write it in RPN: C. Note that this is very similar to one of the expressions in the previous paragraph, only the multiplication and the addition changed places. When you have problems with RPN or when rrdtool is complaining, it's usually a Good Thing to write down the stack on a piece of paper and see what happens. Have the manual ready and pretend to be rrdtool. Just do all the math by hand to see what happens, I'm sure this will solve most, if not all, problems you encounter. =head1 Some special numbers =head2 The unknown value Sometimes collecting your data will fail. This can be very common, especially when querying over busy links. RRDtool can be configured to allow for one (or even more) unknown value and calculate the missing update. You can, for instance, query your device every minute. This is creating one so called PDP or primary data point per minute. If you defined your RRD to contain an RRA that stores 5-minute values, you need five of those PDPs to create one CDP (consolidated data point). These PDPs can become unknown in two cases: =over 4 =item 1. The updates are too far apart. This is tuned using the "heartbeat" setting =item 2. The update was set to unknown on purpose by inserting no value (using the template option) or by using "U" as the value to insert. =back When a CDP is calculated, another mechanism determines if this CDP is valid or not. If there are too many PDPs unknown, the CDP is unknown as well. This is determined by the xff factor. Please note that one unknown counter update can result in two unknown PDPs! If you only allow for one unknown PDP per CDP, this makes the CDP go unknown! Suppose the counter increments with one per second and you retrieve it every minute: counter value resulting rate 10000 10060 1; (10060-10000)/60 == 1 10120 1; (10120-10060)/60 == 1 unknown unknown; you don't know the last value 10240 unknown; you don't know the previous value 10300 1; (10300-10240)/60 == 1 If the CDP was to be calculated from the last five updates, it would get two unknown PDPs and three known PDPs. If xff would have been set to 0.5 which by the way is a commonly used factor, the CDP would have a known value of 1. If xff would have been set to 0.2 then the resulting CDP would be unknown. You have to decide the proper values for heartbeat, number of PDPs per CDP and the xff factor. As you can see from the previous text they define the behavior of your RRA. =head2 Working with unknown data in your database As you have read in the previous chapter, entries in an RRA can be set to the unknown value. If you do calculations with this type of value, the result has to be unknown too. This means that an expression such as C will be unknown if either a or b is unknown. It would be wrong to just ignore the unknown value and return the value of the other parameter. By doing so, you would assume "unknown" means "zero" and this is not true. There has been a case where somebody was collecting data for over a year. A new piece of equipment was installed, a new RRD was created and the scripts were changed to add a counter from the old database and a counter from the new database. The result was disappointing, a large part of the statistics seemed to have vanished mysteriously ... They of course didn't, values from the old database (known values) were added to values from the new database (unknown values) and the result was unknown. In this case, it is fairly reasonable to use a CDEF that alters unknown data into zero. The counters of the device were unknown (after all, it wasn't installed yet!) but you know that the data rate through the device had to be zero (because of the same reason: it was not installed). There are some examples further on that make this change. =head2 Infinity Infinite data is another form of a special number. It cannot be graphed because by definition you would never reach the infinite value. You could think of positive and negative infinity (I'm not sure if mathematicians will agree) depending on the position relative to zero. RRDtool is capable of representing (-not- graphing!) infinity by stopping at its current maximum (for positive infinity) or minimum (for negative infinity) without knowing this maximum (minimum). Infinity in rrdtool is mostly used to draw an AREA without knowing its vertical dimensions. You can think of it as drawing an AREA with an infinite height and displaying only the part that is visible in the current graph. This is probably a good way to approximate infinity and it sure allows for some neat tricks. See below for examples. =head2 Working with unknown data and infinity Sometimes you would like to discard unknown data and pretend it is zero (or any other value for that matter) and sometimes you would like to pretend that known data is unknown (to discard known-to-be-wrong data). This is why CDEFs have support for unknown data. There are also examples available that show unknown data by using infinity. =head1 Some examples =head2 Example: using a recently created RRD You are keeping statistics on your router for over a year now. Recently you installed an extra router and you would like to show the combined throughput for these two devices. If you just add up the counters from router.rrd and router2.rrd, you will add known data (from router.rrd) to unknown data (from router2.rrd) for the bigger part of your stats. You could solve this in a few ways: =over 4 =item * While creating the new database, fill it with zeros from the start to now. You have to make the database start at or before the least recent time in the other database. =item * Alternately you could use CDEF and alter unknown data to zero. =back Both methods have their pros and cons. The first method is troublesome and if you want to do that you have to figure it out yourself. It is not possible to create a database filled with zeros, you have to put them in on purpose. Implementing the second method is described next: What we want is: "if the value is unknown, replace it with zero". This could be writte in pseudo-code as: if (value is unknown) then (zero) else (value). When reading the rrdgraph manual you notice the "UN" function that returns zero or one. You also notice the "IF" function that takes zero or one as input. First look at the "IF" function. It takes three values from the stack, the first value is the decision point, the second value is returned to the stack if the evaluation is "true" and if not, the third value is returned to the stack. We want the "UN" function to decide what happens so we combine those two functions in one CDEF. Lets write down the two possible paths for the "IF" function: if true return a if false return b In RPN: C where "x" is either true or false. Now we have to fill in "x", this should be the "(value is unknown)" part and this is in RPN: C We now combine them: C and when we fill in the appropriate things for "a" and "b" we're finished: C You may want to read Steve Raders RPN guide if you have difficulties with the way I explained this last example. If you want to check this RPN expression, just mimic rrdtools behavior: For any known value, the expression evaluates as follows: CDEF:result=value,UN,0,value,IF (value,UN) is not true so it becomes 0 CDEF:result=0,0,value,IF "IF" will return the 3rd value CDEF:result=value The known value is returned For the unknown value, this happens: CDEF:result=value,UN,0,value,IF (value,UN) is true so it becomes 1 CDEF:result=1,0,value,IF "IF" sees 1 and returns the 2nd value CDEF:result=0 Zero is returned Of course, if you would like to see another value instead of zero, you can use that other value. Eventually, when all unknown data is removed from the RRD, you may want to remove this rule so that unknown data is properly displayed. =head2 Example: better handling of unknown data, by using time Above example has one drawback. If you do log unknown data in your database after installing your new equipment, it will also be translated into zero and therefore you won't see that there was a problem. This is not good and what you really want to do is: =over 4 =item * If there is unknown data, look at the time that this sample was taken =item * If the unknown value is before time xxx, make it zero =item * If it is after time xxx, leave it as unknown data =back This is doable: you can compare the time that the sample was taken to some known time. Assuming you started to monitor your device on Friday September 17, 00:35:57 MET DST. Translate this time in seconds since 1970-01-01 and it becomes 937521357. If you process unknown values that were received after this time, you want to leave them unknown and if they were "received" before this time, you want to translate them into zero (so you can effectively ignore them while adding them to your other routers counters). Translating Friday September 17, 00:35:57 MET DST into 937521357 can be done by, for instance, using gnu date: date -d "19990917 00:35:57" +%s You could also dump the database and see where the data starts to be known. There are several other ways of doing this, just pick one. Now we have to create the magic that allows us to process unknown values different depending on the time that the sample was taken. This is a three step process: =over 4 =item 1. If the timestamp of the value is after 937521357, leave it as is =item 2. If the value is a known value, leave it as is =item 3. Change the unknown value into zero. =back Lets look at part one: if (true) return the original value We rewrite this: if (true) return "a" if (false) return "b" We need to calculate true or false from step 1. There is a function available that returns the timestamp for the current sample. It is called, how surprisingly, "TIME". This time has to be compared to a constant number, we need "GT". The output of "GT" is true or false and this is good input to "IF". We want "if (time > 937521357) then (return a) else (return b)". This process was already described toroughly in the previous chapter so lets do it quick: if (x) then a else b where x represents "time>937521357" where a represents the original value where b represents the outcome of the previous example time>937521357 --> TIME,937521357,GT if (x) then a else b --> x,a,b,IF substitute x --> TIME,937521357,GT,a,b,IF substitute a --> TIME,937521357,GT,value,b,IF substitute b --> TIME,937521357,GT,value,value,UN,0,value,IF,IF We end up with: C This looks very complex however as you can see it was not too hard to come up with. =head2 Example: Pretending weird data isn't there Suppose you have a problem that shows up as huge spikes in your graph. You know this happens and why so you decide to work around the problem. Perhaps you're using your network to do a backup at night and by doing so you get almost 10mb/s while the rest of your network activity does not produce numbers higher than 100kb/s. There are two options: =over 4 =item 1. If the number exceeds 100kb/s it is wrong and you want it masked out by changing it into unknown =item 2. You don't want the graph to show more than 100kb/s =back Pseudo code: if (number > 100) then unknown else number or Pseudo code: if (number > 100) then 100 else number. The second "problem" may also be solved by using the rigid option of rrdtool graph, however this has not the same result. In this example you can end up with a graph that does autoscaling. Also, if you use the numbers to display maxima they will be set to 100kb/s. We use "IF" and "GT" again. "if (x) then (y) else (z)" is written down as "CDEF:result=x,y,z,IF"; now fill in x, y and z. For x you fill in "number greater than 100kb/s" becoming "number,100000,GT" (kilo is 1000 and b/s is what we measure!). The "z" part is "number" in both cases and the "y" part is either "UNKN" for unknown or "100000" for 100kb/s. The two CDEF expressions would be: CDEF:result=number,100000,GT,UNKN,number,IF CDEF:result=number,100000,GT,100000,number,IF =head2 Example: working on a certain time span If you want a graph that spans a few weeks, but would only want to see some routers data for one week, you need to "hide" the rest of the time frame. Don't ask me when this would be useful, it's just here for the example :) We need to compare the time stamp to a begin date and an end date. Comparing isn't difficult: TIME,begintime,GE TIME,endtime,LE These two parts of the CDEF produce either 0 for false or 1 for true. We can now check if they are both 0 (or 1) using a few IF statements but, as Wataru Satoh pointed out, we can use the "*" or "+" functions as locical AND and locical OR. For "*", the result will be zero (false) if either one of the two operators is zero. For "+", the result will only be false (0) when two false (0) operators will be added. Warning: *any* number not equal to 0 will be considered "true". This means that, for instance, "-1,1,+" (which should be "true or true") will become FALSE ... In other words, use "+" only if you know for sure that you have positive numbers (or zero) only. Let's compile the complete CDEF: DEF:ds0=router1.rrd:AVERAGE CDEF:ds0modified=TIME,begintime,GE,TIME,endtime,LE,*,UNKN,ds0,IF This will return the value of ds0 if both comparisons return true. You could also do it the other way around: DEF:ds0=router1.rrd:AVERAGE CDEF:ds0modified=TIME,begintime,LT,TIME,endtime,GT,+,UNKN,ds0,IF This will return an UNKNOWN if either comparison returns true. =head2 Example: You suspect to have problems and want to see unknown data. Suppose you add up the number of active users on several terminal servers. If one of them doesn't give an answer (or an incorrect one) you get "NaN" in the database ("Not a Number") and NaN is evaluated as Unknown. In this case, you would like to be alerted to it and the sum of the remaining values is of no value to you. It would be something like: DEF:users1=location1.rrd:onlineTS1:LAST DEF:users2=location1.rrd:onlineTS2:LAST DEF:users3=location2.rrd:onlineTS1:LAST DEF:users4=location2.rrd:onlineTS2:LAST CDEF:allusers=users1,users2,users3,users4,+,+,+ If you now plot allusers, unknown data in one of users1..users4 will show up as a gap in your graph. You want to modify this to show a bright red line, not a gap. Define an extra CDEF that is unknown if all is okay and is infinite if there is an unknown value: CDEF:wrongdata=allusers,UN,INF,UNKN,IF "allusers,UN" will evaluate to either true or false, it is the (x) part of the "IF" function and it checks if allusers is unknown. The (y) part of the "IF" function is set to "INF" (which means infinity) and the (z) part of the function returns "UNKN". The logic is: if (allusers == unknown) then return INF else return UNKN. You can now use AREA to display this "wrongdata" in bright red. If it is unknown (because allusers is known) then the red AREA won't show up. If the value is INF (because allusers is unknown) then the red AREA will be filled in on the graph at that particular time. AREA:allusers#0000FF:combined user count AREA:wrongdata#FF0000:unknown data =head2 Same example useful with STACKed data: If you use stack in the previous example (as I would do) then you don't add up the values. Therefore, there is no relationship between the four values and you don't get a single value to test. Suppose users3 would be unknown at one point in time: users1 is plotted, users2 is stacked on top of users1, users3 is unknown and therefore nothing happens, users4 is stacked on top of users2. Add the extra CDEFs anyway and use them to overlay the "normal" graph: DEF:users1=location1.rrd:onlineTS1:LAST DEF:users2=location1.rrd:onlineTS2:LAST DEF:users3=location2.rrd:onlineTS1:LAST DEF:users4=location2.rrd:onlineTS2:LAST CDEF:allusers=users1,users2,users3,users4,+,+,+ CDEF:wrongdata=allusers,UN,INF,UNKN,IF AREA:users1#0000FF:users at ts1 STACK:users2#00FF00:users at ts2 STACK:users3#00FFFF:users at ts3 STACK:users4#FFFF00:users at ts4 AREA:wrongdata#FF0000:unknown data If there is unknown data in one of users1..users4, the "wrongdata" AREA will be drawn and because it starts at the X-axis and has infinite height it will effectively overwrite the STACKed parts. You could combine the two CDEF lines into one (we don't use "allusers") if you like. But there are good reasons for writting two CDEFS: =over 4 =item * It improves the readability of the script =item * It can be used inside GPRINT to display the total number of users =back If you choose to combine them, you can substitute the "allusers" in the second CDEF with the part after the equal sign from the first line: CDEF:wrongdata=users1,users2,users3,users4,+,+,+,UN,INF,UNKN,IF If you do so, you won't be able to use these next GPRINTs: COMMENT:"Total number of users seen" GPRINT:allusers:MAX:"Maximum: %6.0lf" GPRINT:allusers:MIN:"Minimum: %6.0lf" GPRINT:allusers:AVERAGE:"Average: %6.0lf" GPRINT:allusers:LAST:"Current: %6.0lf\n" =head1 The examples from the rrd graph manual page =head2 Degrees Celcius vs. Degrees Fahrenheit rrdtool graph demo.gif --title="Demo Graph" \ DEF:cel=demo.rrd:exhaust:AVERAGE \ CDEF:far=cel,32,-,0.55555,* \ LINE2:cel#00a000:"D. Celsius" \ LINE2:far#ff0000:"D. Fahrenheit\c" This example gets the DS called "exhaust" from database "demo.rrd" and puts the values in variable "cel". The CDEF used is evaluated as follows: CDEF:far=cel,32,-,0.5555,* 1. push variable "cel" 2. push 32 3. push function "minus" and process it The stack now contains values that are 32 less than "cel" 4. push 0.5555 5. push function "multiply" and process it 6. the resulting value is now "(cel-32)*0.55555" Note that if you take the celcius to fahrenheit function you should be doing "5/9*(cel-32)" so 0.55555 is not exactly correct. It is close enough for this purpose and it saves a calculation. =head2 Changing unknown into zero rrdtool graph demo.gif --title="Demo Graph" \ DEF:idat1=interface1.rrd:ds0:AVERAGE \ DEF:idat2=interface2.rrd:ds0:AVERAGE \ DEF:odat1=interface1.rrd:ds1:AVERAGE \ DEF:odat2=interface2.rrd:ds1:AVERAGE \ CDEF:agginput=idat1,UN,0,idat1,IF,idat2,UN,0,idat2,IF,+,8,* \ CDEF:aggoutput=odat1,UN,0,odat1,IF,odat2,UN,0,odat2,IF,+,8,* \ AREA:agginput#00cc00:Input Aggregate \ LINE1:aggoutput#0000FF:Output Aggregate These two CDEFs are built from several functions. It helps to split them when viewing what they do. Starting with the first CDEF we would get: idat1,UN --> a 0 --> b idat1 --> c if (a) then (b) else (c) The result is therefore "0" if it is true that "idat1" equals "UN". If not, the original value of "idat1" is put back on the stack. Lets call this answer "d". The process is repeated for the next five items on the stack, it is done the same and will return answer "h". The resulting stack is therefore "d,h". The expression has been simplified to "d,h,+,8,*" and it will now be easy to see that we add "d" and "h", and multiply the result with eight. The end result is that we have added "idat1" and "idat2" and in the process we effectively ignored unknown values. The result is multiplied by eight, most likely to convert bytes/s to bits/s. =head2 Infinity demo rrdtool graph example.png --title="INF demo" \ DEF:val1=some.rrd:ds0:AVERAGE \ DEF:val2=some.rrd:ds1:AVERAGE \ DEF:val3=some.rrd:ds2:AVERAGE \ DEF:val4=other.rrd:ds0:AVERAGE \ CDEF:background=val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF \ CDEF:wipeout=val1,val2,val3,val4,+,+,+,UN,INF,UNKN,IF \ AREA:background#F0F0F0 \ AREA:val1#0000FF:Value1 \ STACK:val2#00C000:Value2 \ STACK:val3#FFFF00:Value3 \ STACK:val4#FFC000:Value4 \ AREA:whipeout#FF0000:Unknown This demo demonstrates two ways to use infinity. It is a bit tricky to see what happens in the "background" CDEF. "val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF" This RPN takes the value of "val4" as input and then immediately removes it from the stack using "POP". The stack is now empty but as a side result we now know the time that this sample was taken. This time is put on the stack by the "TIME" function. "TIME,7200,%" takes the modulo of time and 7200 (which is two hours). The resulting value on the stack will be a number in the range from 0 to 7199. For people who don't know the modulo function: it is the remainder after an integer division. If you divide 16 by 3, the answer would be 5 and the remainder would be 1. So, "16,3,%" returns 1. We have the result of "TIME,7200,%" on the stack, lets call this "a". The start of the RPN has become "a,3600,LE" and this checks if "a" is less or equal than "3600". It is true half of the time. We now have to process the rest of the RPN and this is only a simple "IF" function that returns either "INF" or "UNKN" depending on the time. This is returned to variable "background". The second CDEF has been discussed earlyer in this document so we won't do that here. Now you can draw the different layers. Start with the background that is either unknown (nothing to see) or infinite (the whole positive part of the graph gets filled). Next you draw the data on top of this background. It will overlay the background. Suppose one of val1..val4 would be unknown, in that case you end up with only three bars stacked on top of each other. You don't want to see this because the data is only valid when all four variables are valid. This is why you use the second CDEF, it will overlay the data with an AREA so the data cannot be seen anymore. If your data can also have negative values you also need to overwrite the other half of your graph. This can be done in a relatively simple way: what you need is the "wipeout" variable and place a negative sign before it: "CDEF:wipeout2=wipeout,-1,*" =head2 Filtering data You may do some complex data filtering: MEDIAN FILTER: filters shot noise DEF:var=database.rrd:traffic:AVERAGE CDEF:prev1=PREV(var) CDEF:prev2=PREV(prev1) CDEF:prev3=PREV(prev2) CDEF:median=prev1,prev2,prev3,+,+,3,/ LINE3:median#000077:filtered LINE1:prev2#007700:'raw data' DERIVATE: DEF:var=database.rrd:traffic:AVERAGE CDEF:prev1=PREV(var) CDEF:time=TIME CDEF:prevtime=PREV(time) CDEF:derivate=var,prev1,-,time,prevtime,-,/ LINE3:derivate#000077:derivate LINE1:var#007700:'raw data' =head1 Out of ideas for now This document was created from questions asked by either myself or by other people on the list. Please let me know if you find errors in it or if you have trouble understanding it. If you think there should be an addition, mail me: Ealex@ergens.op.het.netE Remember: B =head1 SEE ALSO The RRDtool manpages =head1 AUTHOR Alex van den Bogaerdt Ealex@ergens.op.het.netE RRDtool/RRDtool-DOC/opt/rrd/doc/cdeftutorial.txt010064400000020000002000001405540772067753400213260ustar00binbin00000000000000 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) 1111....0000....44445555 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00006666----22220000 NNNNAAAAMMMMEEEE cdeftutorial - Alex van den Bogaerdt's CDEF tutorial DDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN YYYYoooouuuu pppprrrroooovvvviiiiddddeeee aaaa qqqquuuueeeessssttttiiiioooonnnn aaaannnndddd IIII wwwwiiiillllllll ttttrrrryyyy ttttoooo pppprrrroooovvvviiiiddddeeee aaaannnn aaaannnnsssswwwweeeerrrr iiiinnnn tttthhhheeee nnnneeeexxxxtttt rrrreeeelllleeeeaaaasssseeee. NNNNoooo ffffeeeeeeeeddddbbbbaaaacccckkkk eeeeqqqquuuuaaaallllssss nnnnoooo cccchhhhaaaannnnggggeeeessss!!!! _A_d_d_i_t_i_o_n_s _t_o _t_h_i_s _d_o_c_u_m_e_n_t _a_r_e _a_l_s_o _w_e_l_c_o_m_e. Alex van den Bogaerdt WWWWhhhhyyyy tttthhhhiiiissss ttttuuuuttttoooorrrriiiiaaaallll ???? One of the powerful parts of RRDtool is its ability to do all sorts of calculations on the data retrieved from it's databases. However RRDtool's many options and syntax make it difficult for the average user to understand. The manuals are good at explaining what these options do; however they do not (and should not) explain in detail why they are useful. As with my RRDtool tutorial: if you want a simple document in simple language you should read this tutorial. If you are happy with the official documentation, you may find this document too simple or even boring. If you do choose to read this tutorial, I also expect you to have read and fully understand my other tutorial. MMMMoooorrrreeee rrrreeeeaaaaddddiiiinnnngggg If you have difficulties with the way I try to explain it please read Steve Rader's rpntutorial. It may help you understand how this all works. WWWWhhhhaaaatttt aaaarrrreeee CCCCDDDDEEEEFFFFssss ???? When retrieving data from an RRD, you are using a "DEF" to work with that data. Think of it as a variable that changes over time (where time is the x-axis). The value of this variable is what is found in the database at that particular time and you can't do any modifications on it. This is what CDEFs are for: they takes values from DEFs and perform calculations on them. SSSSyyyynnnnttttaaaaxxxx DEF:var_name_1=some.rrd:ds_name:CF CDEF:var_name_2=RPN_expression You first define "var_name_1" to be data collected from data source "ds_name" found in RRD "some.rrd" with consolidation function "CF". Assume the ifInOctets SNMP counter is saved in mrtg.rrd as the DS "in". Then the following DEF defines a variable for the average of that data source: DEF:inbytes=mrtg.rrd:in:AVERAGE - 1 - Formatted: August 20, 2003 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) 1111....0000....44445555 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00006666----22220000 Say you want to display bits per second (instead of bytes per second as stored in the database.) You have to define a calculation (hence "CDEF") on variable "inbytes" and use that variable (inbits) instead of the original: CDEF:inbits=inbytes,8,* It tells to multiply inbytes by eight to get inbits. I'll explain later how this works. In the graphing or printing functions, you can now use inbits where you would use inbytes otherwise. Note that variable in the CDEF (inbits) must not be the same as the variable (inbytes) in the DEF! RRRRPPPPNNNN----eeeexxxxpppprrrreeeessssssssiiiioooonnnnssss RPN is short-hand for Reverse Polish Notation. It works as follows. You put the variables or numbers on a stack. You also put operations (things-to-do) on the stack and this stack is then processed. The result will be placed on the stack. At the end, there should be exactly one number left: the outcome of the series of operations. If there is not exactly one number left, rrdtool will complain loudly. Above multiplication by eight will look like: 1. Start with an empty stack 2. Put the content of variable inbytes on the stack 3. Put the number eight on the stack 4. Put the operation multiply on the stack 5. Process the stack 6. Retrieve the value from the stack and put it in variable inbits We will now do an example with real numbers. Suppose the variable inbytes would have value 10, the stack would be: 1. || 2. |10| 3. |10|8| 4. |10|8|*| 5. |80| 6. || - 2 - Formatted: August 20, 2003 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) 1111....0000....44445555 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00006666----22220000 Processing the stack (step 5) will retrieve one value from the stack (from the right at step 4). This is the operation multiply and this takes two values off the stack as input. The result is put back on the stack (the value 80 in this case). For multiplication the order doesn't matter but for other operations like subtraction and division it does. Generally speaking you have the following order: y = A - B --> y=minus(A,B) --> CDEF:y=A,B,- This is not very intuitive (at least most people don't think so). For the function f(A,B) you reverse the position of "f" but you do not reverse the order of the variables. CCCCoooonnnnvvvveeeerrrrttttiiiinnnngggg yyyyoooouuuurrrr wwwwiiiisssshhhheeeessss ttttoooo RRRRPPPPNNNN First, get a clear picture of what you want to do. Break down the problem in smaller portions until they cannot be split anymore. Then it is rather simple to convert your ideas into RPN. Suppose you have several RRDs and would like to add up some counters in them. These could be, for instance, the counters for every WAN link you are monitoring. You have: router1.rrd with link1in link2in router2.rrd with link1in link2in router3.rrd with link1in link2in Suppose you would like to add up all these counters, except for link2in inside router2.rrd. You need to do: (in this example, "router1.rrd:link1in" means the DS link1in inside the RRD router1.rrd) router1.rrd:link1in router1.rrd:link2in router2.rrd:link1in router3.rrd:link1in router3.rrd:link2in -------------------- + (outcome of the sum) As a mathmatical function, this could be written: "add(router1.rrd:link1in , router1.rrd:link2in , router2.rrd:link1in , router3.rrd:link1in , router3.rrd:link2.in)" With RRDtool and RPN, first, define the inputs: - 3 - Formatted: August 20, 2003 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) 1111....0000....44445555 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00006666----22220000 DEF:a=router1.rrd:link1in:AVERAGE DEF:b=router1.rrd:link2in:AVERAGE DEF:c=router2.rrd:link1in:AVERAGE DEF:d=router3.rrd:link1in:AVERAGE DEF:e=router3.rrd:link2in:AVERAGE Now, the mathematical function becomes: "add(a,b,c,d,e)" In RPN, there's no operator that sums more than two values so you need to do several additions. You add a and b, add c to the result, add d to the result and add e to the result. push a: a stack contains the value of a push b and add: b,+ stack contains the result of a+b push c and add: c,+ stack contains the result of a+b+c push d and add: d,+ stack contains the result of a+b+c+d push e and add: e,+ stack contains the result of a+b+c+d+e What was calculated here would be written down as: ( ( ( (a+b) + c) + d) + e) > This is in RPN: "CDEF:result=a,b,+,c,+,d,+,e,+" This is correct but it can be made more clear to humans. It does not matter if you add a to b and then add c to the result or first add b to c and then add a to the result. This makes it possible to rewrite the RPN into "CDEF:result=a,b,c,d,e,+,+,+,+" which is evaluatated differently: push value of variable a on the stack: a push value of variable b on the stack: a b push value of variable c on the stack: a b c push value of variable d on the stack: a b c d push value of variable e on the stack: a b c d e push operator + on the stack: a b c d e + and process it: a b c P (where P == d+e) push operator + on the stack: a b c P + and process it: a b Q (where Q == c+P) push operator + on the stack: a b Q + and process it: a R (where R == b+Q) push operator + on the stack: a R + and process it: S (where S == a+R) As you can see the RPN expression "a,b,c,d,e,+,+,+,+,+" will evaluate in "((((d+e)+c)+b)+a)" and it has the same outcome as "a,b,+,c,+,d,+,e,+" According to Steve Rader this is called the commutative law of addition but you may forget this right away, as long as you remember what it represents. - 4 - Formatted: August 20, 2003 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) 1111....0000....44445555 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00006666----22220000 Now look at an expression that contains a multiplication: First in normal math: "let result = a+b*c". In this case you can't choose the order yourself, you have to start with the multiplication and then add a to it. You may alter the position of b and c, you may not alter the position of a and b. You have to take this in consideration when converting this expression into RPN. Read it as: "Add the outcome of b*c to a" and then it is easy to write the RPN expression: "result=a,b,c,*,+" Another expression that would return the same: "result=b,c,*,a,+" In normal math, you may encounter something like "a*(b+c)" and this can also be converted into RPN. The parenthesis just tell you to first add b and c, and then multiply a with the result. Again, now it is easy to write it in RPN: "result=a,b,c,+,*". Note that this is very similar to one of the expressions in the previous paragraph, only the multiplication and the addition changed places. When you have problems with RPN or when rrdtool is complaining, it's usually a Good Thing to write down the stack on a piece of paper and see what happens. Have the manual ready and pretend to be rrdtool. Just do all the math by hand to see what happens, I'm sure this will solve most, if not all, problems you encounter. SSSSoooommmmeeee ssssppppeeeecccciiiiaaaallll nnnnuuuummmmbbbbeeeerrrrssss TTTThhhheeee uuuunnnnkkkknnnnoooowwwwnnnn vvvvaaaalllluuuueeee Sometimes collecting your data will fail. This can be very common, especially when querying over busy links. RRDtool can be configured to allow for one (or even more) unknown value and calculate the missing update. You can, for instance, query your device every minute. This is creating one so called PDP or primary data point per minute. If you defined your RRD to contain an RRA that stores 5-minute values, you need five of those PDPs to create one CDP (consolidated data point). These PDPs can become unknown in two cases: 1. The updates are too far apart. This is tuned using the "heartbeat" setting 2. The update was set to unknown on purpose by inserting no value (using the template option) or by using "U" as the value to insert. When a CDP is calculated, another mechanism determines if this CDP is valid or not. If there are too many PDPs unknown, the CDP is unknown as well. This is determined by the xff factor. Please note that one unknown counter update can result in two unknown PDPs! If you only allow for one unknown PDP per CDP, this makes the CDP go unknown! - 5 - Formatted: August 20, 2003 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) 1111....0000....44445555 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00006666----22220000 Suppose the counter increments with one per second and you retrieve it every minute: counter value resulting rate 10000 10060 1; (10060-10000)/60 == 1 10120 1; (10120-10060)/60 == 1 unknown unknown; you don't know the last value 10240 unknown; you don't know the previous value 10300 1; (10300-10240)/60 == 1 If the CDP was to be calculated from the last five updates, it would get two unknown PDPs and three known PDPs. If xff would have been set to 0.5 which by the way is a commonly used factor, the CDP would have a known value of 1. If xff would have been set to 0.2 then the resulting CDP would be unknown. You have to decide the proper values for heartbeat, number of PDPs per CDP and the xff factor. As you can see from the previous text they define the behavior of your RRA. WWWWoooorrrrkkkkiiiinnnngggg wwwwiiiitttthhhh uuuunnnnkkkknnnnoooowwwwnnnn ddddaaaattttaaaa iiiinnnn yyyyoooouuuurrrr ddddaaaattttaaaabbbbaaaasssseeee As you have read in the previous chapter, entries in an RRA can be set to the unknown value. If you do calculations with this type of value, the result has to be unknown too. This means that an expression such as "result=a,b,+" will be unknown if either a or b is unknown. It would be wrong to just ignore the unknown value and return the value of the other parameter. By doing so, you would assume "unknown" means "zero" and this is not true. There has been a case where somebody was collecting data for over a year. A new piece of equipment was installed, a new RRD was created and the scripts were changed to add a counter from the old database and a counter from the new database. The result was disappointing, a large part of the statistics seemed to have vanished mysteriously ... They of course didn't, values from the old database (known values) were added to values from the new database (unknown values) and the result was unknown. In this case, it is fairly reasonable to use a CDEF that alters unknown data into zero. The counters of the device were unknown (after all, it wasn't installed yet!) but you know that the data rate through the device had to be zero (because of the same reason: it was not installed). There are some examples further on that make this change. - 6 - Formatted: August 20, 2003 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) 1111....0000....44445555 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00006666----22220000 IIIInnnnffffiiiinnnniiiittttyyyy Infinite data is another form of a special number. It cannot be graphed because by definition you would never reach the infinite value. You could think of positive and negative infinity (I'm not sure if mathematicians will agree) depending on the position relative to zero. RRDtool is capable of representing (-not- graphing!) infinity by stopping at its current maximum (for positive infinity) or minimum (for negative infinity) without knowing this maximum (minimum). Infinity in rrdtool is mostly used to draw an AREA without knowing its vertical dimensions. You can think of it as drawing an AREA with an infinite height and displaying only the part that is visible in the current graph. This is probably a good way to approximate infinity and it sure allows for some neat tricks. See below for examples. WWWWoooorrrrkkkkiiiinnnngggg wwwwiiiitttthhhh uuuunnnnkkkknnnnoooowwwwnnnn ddddaaaattttaaaa aaaannnndddd iiiinnnnffffiiiinnnniiiittttyyyy Sometimes you would like to discard unknown data and pretend it is zero (or any other value for that matter) and sometimes you would like to pretend that known data is unknown (to discard known-to-be-wrong data). This is why CDEFs have support for unknown data. There are also examples available that show unknown data by using infinity. SSSSoooommmmeeee eeeexxxxaaaammmmpppplllleeeessss EEEExxxxaaaammmmpppplllleeee:::: uuuussssiiiinnnngggg aaaa rrrreeeecccceeeennnnttttllllyyyy ccccrrrreeeeaaaatttteeeedddd RRRRRRRRDDDD You are keeping statistics on your router for over a year now. Recently you installed an extra router and you would like to show the combined throughput for these two devices. If you just add up the counters from router.rrd and router2.rrd, you will add known data (from router.rrd) to unknown data (from router2.rrd) for the bigger part of your stats. You could solve this in a few ways: o+ While creating the new database, fill it with zeros from the start to now. You have to make the database start at or before the least recent time in the other database. o+ Alternately you could use CDEF and alter unknown data to zero. Both methods have their pros and cons. The first method is troublesome and if you want to do that you have to figure it out yourself. It is not possible to create a database filled with zeros, you have to put them in on purpose. Implementing the second method is described next: What we want is: "if the value is unknown, replace it with zero". This - 7 - Formatted: August 20, 2003 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) 1111....0000....44445555 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00006666----22220000 could be writte in pseudo-code as: if (value is unknown) then (zero) else (value). When reading the rrdgraph manual you notice the "UN" function that returns zero or one. You also notice the "IF" function that takes zero or one as input. First look at the "IF" function. It takes three values from the stack, the first value is the decision point, the second value is returned to the stack if the evaluation is "true" and if not, the third value is returned to the stack. We want the "UN" function to decide what happens so we combine those two functions in one CDEF. Lets write down the two possible paths for the "IF" function: if true return a if false return b In RPN: "result=x,a,b,IF" where "x" is either true or false. Now we have to fill in "x", this should be the "(value is unknown)" part and this is in RPN: "result=value,UN" We now combine them: "result=value,UN,a,b,IF" and when we fill in the appropriate things for "a" and "b" we're finished: "CDEF:result=value,UN,0,value,IF" You may want to read Steve Raders RPN guide if you have difficulties with the way I explained this last example. If you want to check this RPN expression, just mimic rrdtools behavior: For any known value, the expression evaluates as follows: CDEF:result=value,UN,0,value,IF (value,UN) is not true so it becomes 0 CDEF:result=0,0,value,IF "IF" will return the 3rd value CDEF:result=value The known value is returned For the unknown value, this happens: CDEF:result=value,UN,0,value,IF (value,UN) is true so it becomes 1 CDEF:result=1,0,value,IF "IF" sees 1 and returns the 2nd value CDEF:result=0 Zero is returned Of course, if you would like to see another value instead of zero, you can use that other value. Eventually, when all unknown data is removed from the RRD, you may want to remove this rule so that unknown data is properly displayed. - 8 - Formatted: August 20, 2003 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) 1111....0000....44445555 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00006666----22220000 EEEExxxxaaaammmmpppplllleeee:::: bbbbeeeetttttttteeeerrrr hhhhaaaannnnddddlllliiiinnnngggg ooooffff uuuunnnnkkkknnnnoooowwwwnnnn ddddaaaattttaaaa,,,, bbbbyyyy uuuussssiiiinnnngggg ttttiiiimmmmeeee Above example has one drawback. If you do log unknown data in your database after installing your new equipment, it will also be translated into zero and therefore you won't see that there was a problem. This is not good and what you really want to do is: o+ If there is unknown data, look at the time that this sample was taken o+ If the unknown value is before time xxx, make it zero o+ If it is after time xxx, leave it as unknown data This is doable: you can compare the time that the sample was taken to some known time. Assuming you started to monitor your device on Friday September 17, 00:35:57 MET DST. Translate this time in seconds since 1970-01-01 and it becomes 937521357. If you process unknown values that were received after this time, you want to leave them unknown and if they were "received" before this time, you want to translate them into zero (so you can effectively ignore them while adding them to your other routers counters). Translating Friday September 17, 00:35:57 MET DST into 937521357 can be done by, for instance, using gnu date: date -d "19990917 00:35:57" +%s You could also dump the database and see where the data starts to be known. There are several other ways of doing this, just pick one. Now we have to create the magic that allows us to process unknown values different depending on the time that the sample was taken. This is a three step process: 1. If the timestamp of the value is after 937521357, leave it as is 2. If the value is a known value, leave it as is 3. Change the unknown value into zero. Lets look at part one: if (true) return the original value We rewrite this: if (true) return "a" if (false) return "b" - 9 - Formatted: August 20, 2003 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) 1111....0000....44445555 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00006666----22220000 We need to calculate true or false from step 1. There is a function available that returns the timestamp for the current sample. It is called, how surprisingly, "TIME". This time has to be compared to a constant number, we need "GT". The output of "GT" is true or false and this is good input to "IF". We want "if (time > 937521357) then (return a) else (return b)". This process was already described toroughly in the previous chapter so lets do it quick: if (x) then a else b where x represents "time>937521357" where a represents the original value where b represents the outcome of the previous example time>937521357 --> TIME,937521357,GT if (x) then a else b --> x,a,b,IF substitute x --> TIME,937521357,GT,a,b,IF substitute a --> TIME,937521357,GT,value,b,IF substitute b --> TIME,937521357,GT,value,value,UN,0,value,IF,IF We end up with: "CDEF:result=TIME,937521357,GT,value,value,UN,0,value,IF,IF" This looks very complex however as you can see it was not too hard to come up with. EEEExxxxaaaammmmpppplllleeee:::: PPPPrrrreeeetttteeeennnnddddiiiinnnngggg wwwweeeeiiiirrrrdddd ddddaaaattttaaaa iiiissssnnnn''''tttt tttthhhheeeerrrreeee Suppose you have a problem that shows up as huge spikes in your graph. You know this happens and why so you decide to work around the problem. Perhaps you're using your network to do a backup at night and by doing so you get almost 10mb/s while the rest of your network activity does not produce numbers higher than 100kb/s. There are two options: 1. If the number exceeds 100kb/s it is wrong and you want it masked out by changing it into unknown 2. You don't want the graph to show more than 100kb/s Pseudo code: if (number > 100) then unknown else number or Pseudo code: if (number > 100) then 100 else number. The second "problem" may also be solved by using the rigid option of rrdtool graph, however this has not the same result. In this example you can end up with a graph that does autoscaling. Also, if you use the numbers to display maxima they will be set to 100kb/s. - 10 - Formatted: August 20, 2003 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) 1111....0000....44445555 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00006666----22220000 We use "IF" and "GT" again. "if (x) then (y) else (z)" is written down as "CDEF:result=x,y,z,IF"; now fill in x, y and z. For x you fill in "number greater than 100kb/s" becoming "number,100000,GT" (kilo is 1000 and b/s is what we measure!). The "z" part is "number" in both cases and the "y" part is either "UNKN" for unknown or "100000" for 100kb/s. The two CDEF expressions would be: CDEF:result=number,100000,GT,UNKN,number,IF CDEF:result=number,100000,GT,100000,number,IF EEEExxxxaaaammmmpppplllleeee:::: wwwwoooorrrrkkkkiiiinnnngggg oooonnnn aaaa cccceeeerrrrttttaaaaiiiinnnn ttttiiiimmmmeeee ssssppppaaaannnn If you want a graph that spans a few weeks, but would only want to see some routers data for one week, you need to "hide" the rest of the time frame. Don't ask me when this would be useful, it's just here for the example :) We need to compare the time stamp to a begin date and an end date. Comparing isn't difficult: TIME,begintime,GE TIME,endtime,LE These two parts of the CDEF produce either 0 for false or 1 for true. We can now check if they are both 0 (or 1) using a few IF statements but, as Wataru Satoh pointed out, we can use the "*" or "+" functions as locical AND and locical OR. For "*", the result will be zero (false) if either one of the two operators is zero. For "+", the result will only be false (0) when two false (0) operators will be added. Warning: *any* number not equal to 0 will be considered "true". This means that, for instance, "-1,1,+" (which should be "true or true") will become FALSE ... In other words, use "+" only if you know for sure that you have positive numbers (or zero) only. Let's compile the complete CDEF: DEF:ds0=router1.rrd:AVERAGE CDEF:ds0modified=TIME,begintime,GE,TIME,endtime,LE,*,UNKN,ds0,IF This will return the value of ds0 if both comparisons return true. You could also do it the other way around: DEF:ds0=router1.rrd:AVERAGE CDEF:ds0modified=TIME,begintime,LT,TIME,endtime,GT,+,UNKN,ds0,IF This will return an UNKNOWN if either comparison returns true. - 11 - Formatted: August 20, 2003 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) 1111....0000....44445555 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00006666----22220000 EEEExxxxaaaammmmpppplllleeee:::: YYYYoooouuuu ssssuuuussssppppeeeecccctttt ttttoooo hhhhaaaavvvveeee pppprrrroooobbbblllleeeemmmmssss aaaannnndddd wwwwaaaannnntttt ttttoooo sssseeeeeeee uuuunnnnkkkknnnnoooowwwwnnnn ddddaaaattttaaaa.... Suppose you add up the number of active users on several terminal servers. If one of them doesn't give an answer (or an incorrect one) you get "NaN" in the database ("Not a Number") and NaN is evaluated as Unknown. In this case, you would like to be alerted to it and the sum of the remaining values is of no value to you. It would be something like: DEF:users1=location1.rrd:onlineTS1:LAST DEF:users2=location1.rrd:onlineTS2:LAST DEF:users3=location2.rrd:onlineTS1:LAST DEF:users4=location2.rrd:onlineTS2:LAST CDEF:allusers=users1,users2,users3,users4,+,+,+ If you now plot allusers, unknown data in one of users1..users4 will show up as a gap in your graph. You want to modify this to show a bright red line, not a gap. Define an extra CDEF that is unknown if all is okay and is infinite if there is an unknown value: CDEF:wrongdata=allusers,UN,INF,UNKN,IF "allusers,UN" will evaluate to either true or false, it is the (x) part of the "IF" function and it checks if allusers is unknown. The (y) part of the "IF" function is set to "INF" (which means infinity) and the (z) part of the function returns "UNKN". The logic is: if (allusers == unknown) then return INF else return UNKN. You can now use AREA to display this "wrongdata" in bright red. If it is unknown (because allusers is known) then the red AREA won't show up. If the value is INF (because allusers is unknown) then the red AREA will be filled in on the graph at that particular time. AREA:allusers#0000FF:combined user count AREA:wrongdata#FF0000:unknown data SSSSaaaammmmeeee eeeexxxxaaaammmmpppplllleeee uuuusssseeeeffffuuuullll wwwwiiiitttthhhh SSSSTTTTAAAACCCCKKKKeeeedddd ddddaaaattttaaaa:::: If you use stack in the previous example (as I would do) then you don't add up the values. Therefore, there is no relationship between the four values and you don't get a single value to test. Suppose users3 would be unknown at one point in time: users1 is plotted, users2 is stacked on top of users1, users3 is unknown and therefore - 12 - Formatted: August 20, 2003 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) 1111....0000....44445555 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00006666----22220000 nothing happens, users4 is stacked on top of users2. Add the extra CDEFs anyway and use them to overlay the "normal" graph: DEF:users1=location1.rrd:onlineTS1:LAST DEF:users2=location1.rrd:onlineTS2:LAST DEF:users3=location2.rrd:onlineTS1:LAST DEF:users4=location2.rrd:onlineTS2:LAST CDEF:allusers=users1,users2,users3,users4,+,+,+ CDEF:wrongdata=allusers,UN,INF,UNKN,IF AREA:users1#0000FF:users at ts1 STACK:users2#00FF00:users at ts2 STACK:users3#00FFFF:users at ts3 STACK:users4#FFFF00:users at ts4 AREA:wrongdata#FF0000:unknown data If there is unknown data in one of users1..users4, the "wrongdata" AREA will be drawn and because it starts at the X-axis and has infinite height it will effectively overwrite the STACKed parts. You could combine the two CDEF lines into one (we don't use "allusers") if you like. But there are good reasons for writting two CDEFS: o+ It improves the readability of the script o+ It can be used inside GPRINT to display the total number of users If you choose to combine them, you can substitute the "allusers" in the second CDEF with the part after the equal sign from the first line: CDEF:wrongdata=users1,users2,users3,users4,+,+,+,UN,INF,UNKN,IF If you do so, you won't be able to use these next GPRINTs: COMMENT:"Total number of users seen" GPRINT:allusers:MAX:"Maximum: %6.0lf" GPRINT:allusers:MIN:"Minimum: %6.0lf" GPRINT:allusers:AVERAGE:"Average: %6.0lf" GPRINT:allusers:LAST:"Current: %6.0lf\n" TTTThhhheeee eeeexxxxaaaammmmpppplllleeeessss ffffrrrroooommmm tttthhhheeee rrrrrrrrdddd ggggrrrraaaapppphhhh mmmmaaaannnnuuuuaaaallll ppppaaaaggggeeee DDDDeeeeggggrrrreeeeeeeessss CCCCeeeellllcccciiiiuuuussss vvvvssss.... DDDDeeeeggggrrrreeeeeeeessss FFFFaaaahhhhrrrreeeennnnhhhheeeeiiiitttt rrdtool graph demo.gif --title="Demo Graph" \ DEF:cel=demo.rrd:exhaust:AVERAGE \ CDEF:far=cel,32,-,0.55555,* \ LINE2:cel#00a000:"D. Celsius" \ LINE2:far#ff0000:"D. Fahrenheit\c" - 13 - Formatted: August 20, 2003 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) 1111....0000....44445555 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00006666----22220000 This example gets the DS called "exhaust" from database "demo.rrd" and puts the values in variable "cel". The CDEF used is evaluated as follows: CDEF:far=cel,32,-,0.5555,* 1. push variable "cel" 2. push 32 3. push function "minus" and process it The stack now contains values that are 32 less than "cel" 4. push 0.5555 5. push function "multiply" and process it 6. the resulting value is now "(cel-32)*0.55555" Note that if you take the celcius to fahrenheit function you should be doing "5/9*(cel-32)" so 0.55555 is not exactly correct. It is close enough for this purpose and it saves a calculation. CCCChhhhaaaannnnggggiiiinnnngggg uuuunnnnkkkknnnnoooowwwwnnnn iiiinnnnttttoooo zzzzeeeerrrroooo rrdtool graph demo.gif --title="Demo Graph" \ DEF:idat1=interface1.rrd:ds0:AVERAGE \ DEF:idat2=interface2.rrd:ds0:AVERAGE \ DEF:odat1=interface1.rrd:ds1:AVERAGE \ DEF:odat2=interface2.rrd:ds1:AVERAGE \ CDEF:agginput=idat1,UN,0,idat1,IF,idat2,UN,0,idat2,IF,+,8,* \ CDEF:aggoutput=odat1,UN,0,odat1,IF,odat2,UN,0,odat2,IF,+,8,* \ AREA:agginput#00cc00:Input Aggregate \ LINE1:aggoutput#0000FF:Output Aggregate These two CDEFs are built from several functions. It helps to split them when viewing what they do. Starting with the first CDEF we would get: idat1,UN --> a 0 --> b idat1 --> c if (a) then (b) else (c) The result is therefore "0" if it is true that "idat1" equals "UN". If not, the original value of "idat1" is put back on the stack. Lets call this answer "d". The process is repeated for the next five items on the stack, it is done the same and will return answer "h". The resulting stack is therefore "d,h". The expression has been simplified to "d,h,+,8,*" and it will now be easy to see that we add "d" and "h", and multiply the result with eight. The end result is that we have added "idat1" and "idat2" and in the process we effectively ignored unknown values. The result is multiplied by eight, most likely to convert bytes/s to bits/s. - 14 - Formatted: August 20, 2003 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) 1111....0000....44445555 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00006666----22220000 IIIInnnnffffiiiinnnniiiittttyyyy ddddeeeemmmmoooo rrdtool graph example.png --title="INF demo" \ DEF:val1=some.rrd:ds0:AVERAGE \ DEF:val2=some.rrd:ds1:AVERAGE \ DEF:val3=some.rrd:ds2:AVERAGE \ DEF:val4=other.rrd:ds0:AVERAGE \ CDEF:background=val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF \ CDEF:wipeout=val1,val2,val3,val4,+,+,+,UN,INF,UNKN,IF \ AREA:background#F0F0F0 \ AREA:val1#0000FF:Value1 \ STACK:val2#00C000:Value2 \ STACK:val3#FFFF00:Value3 \ STACK:val4#FFC000:Value4 \ AREA:whipeout#FF0000:Unknown This demo demonstrates two ways to use infinity. It is a bit tricky to see what happens in the "background" CDEF. "val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF" This RPN takes the value of "val4" as input and then immediately removes it from the stack using "POP". The stack is now empty but as a side result we now know the time that this sample was taken. This time is put on the stack by the "TIME" function. "TIME,7200,%" takes the modulo of time and 7200 (which is two hours). The resulting value on the stack will be a number in the range from 0 to 7199. For people who don't know the modulo function: it is the remainder after an integer division. If you divide 16 by 3, the answer would be 5 and the remainder would be 1. So, "16,3,%" returns 1. We have the result of "TIME,7200,%" on the stack, lets call this "a". The start of the RPN has become "a,3600,LE" and this checks if "a" is less or equal than "3600". It is true half of the time. We now have to process the rest of the RPN and this is only a simple "IF" function that returns either "INF" or "UNKN" depending on the time. This is returned to variable "background". The second CDEF has been discussed earlyer in this document so we won't do that here. Now you can draw the different layers. Start with the background that is either unknown (nothing to see) or infinite (the whole positive part of the graph gets filled). Next you draw the data on top of this background. It will overlay the background. Suppose one of val1..val4 would be unknown, in that case you end up with only three bars stacked on top of each other. You don't want to see this because the data is - 15 - Formatted: August 20, 2003 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) 1111....0000....44445555 CCCCDDDDEEEEFFFFTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00006666----22220000 only valid when all four variables are valid. This is why you use the second CDEF, it will overlay the data with an AREA so the data cannot be seen anymore. If your data can also have negative values you also need to overwrite the other half of your graph. This can be done in a relatively simple way: what you need is the "wipeout" variable and place a negative sign before it: "CDEF:wipeout2=wipeout,-1,*" FFFFiiiilllltttteeeerrrriiiinnnngggg ddddaaaattttaaaa You may do some complex data filtering: MEDIAN FILTER: filters shot noise DEF:var=database.rrd:traffic:AVERAGE CDEF:prev1=PREV(var) CDEF:prev2=PREV(prev1) CDEF:prev3=PREV(prev2) CDEF:median=prev1,prev2,prev3,+,+,3,/ LINE3:median#000077:filtered LINE1:prev2#007700:'raw data' DERIVATE: DEF:var=database.rrd:traffic:AVERAGE CDEF:prev1=PREV(var) CDEF:time=TIME CDEF:prevtime=PREV(time) CDEF:derivate=var,prev1,-,time,prevtime,-,/ LINE3:derivate#000077:derivate LINE1:var#007700:'raw data' OOOOuuuutttt ooooffff iiiiddddeeeeaaaassss ffffoooorrrr nnnnoooowwww This document was created from questions asked by either myself or by other people on the list. Please let me know if you find errors in it or if you have trouble understanding it. If you think there should be an addition, mail me: Remember: NNNNoooo ffffeeeeeeeeddddbbbbaaaacccckkkk eeeeqqqquuuuaaaallllssss nnnnoooo cccchhhhaaaannnnggggeeeessss!!!! SSSSEEEEEEEE AAAALLLLSSSSOOOO The RRDtool manpages AAAAUUUUTTTTHHHHOOOORRRR Alex van den Bogaerdt - 16 - Formatted: August 20, 2003 RRDtool/RRDtool-DOC/opt/rrd/doc/rpntutorial.pod010064400000020000002000000152610772067753400211630ustar00binbin00000000000000=head1 NAME rpntutorial - Reading RRDTtool RPN Expressions by Steve Rader =for html
PDF version.
=head1 DESCRIPTION This tutorial should help you get to grips with rrdtool RPN expressions as seen in CDEF arguments of rrdtool graph. =head1 Reading Comparison Operators The LT, LE, GT, GE and EQ RPN logic operators are not as tricky as they appear. These operators act on the two values on the stack preceding them (to the left). Read these two values on the stack from left to right inserting the operator in the middle. If the resulting statement is true, the replace the three values from the stack with "1". If the statement if false, replace the three values with "0". For example think about "2,1,GT". This RPN expression could be read as "is two greater than one?" The answer to that question is "true". So the three values should be replaced with "1". Thus the RPN expression 2,1,GT evaluates to 1. Now also consider "2,1,LE". This RPN expression could be read as "is two less than or equal to one?". The natural response is "no" and thus the RPN expression 2,1,LE evaluates to 0. =head1 Reading the IF Operator The IF RPN logic operator can be straightforward also. The key to reading IF operators is to understand that the condition part of the traditional "if X than Y else Z" notation has *already* been evaluated. So the IF operator acts on only one value on the stack: the third value to the left of the IF value. The second value to the left of the IF corresponds to the true ("Y") branch. And the first value to the left of the IF corresponds to the false ("Z") branch. Read the RPN expression "X,Y,Z,IF" from left to right like so: "if X then Y else Z". For example, consider "1,10,100,IF". It looks bizzare to me. But when I read "if 1 then 10 else 100" it's crystal clear: 1 is true so the answer is 10. Note that only zero is false; all other values are true. "2,20,200,IF" ("if 2 then 20 else 200") evaluates to 20. And "0,1,2,IF" ("if 0 then 1 else 2) evaluates to 2. Notice that none of the above examples really simulate the whole "if X then Y else Z" statement. This is because computer programmers read this statement as "if Some Condition then Y else Z". So it's important to be able to read IF operators along with the LT, LE, GT, GE and EQ operators. =head1 Some Examples While compound expressions can look overly complex, they can be considered elegantly simple. To quickly comprehend RPN expressions, you must know the the algorithm for evaluating RPN expressions: iterate searches from the left to the right looking for an operator, when it's found, apply that operator by popping the operator and some number of values (and by definition, not operators) off the stack. For example, the stack "1,2,3,+,+" gets "2,3,+" evaluated (as "2+3") during the first iteration which is replaced by 5. This results in the stack "1,5,+". Finally, "1,5,+" is evaluated resulting in the answer 6. For convenience sake, it's useful to write this set of operations as: 1) 1,2,3,+,+ eval is 2,3,+ = 5 result is 1,5,+ 2) 1,5,+ eval is 1,5,+ = 6 result is 6 3) 6 Let's use that notation to conviently solve some complex RPN expressions with multiple logic operators: 1) 20,10,GT,10,20,IF eval is 20,10,GT = 1 result is 1,10,20,IF read the eval as pop "20 is greater than 10" so push 1 2) 1,10,20,IF eval is 1,10,20,IF = 10 result is 10 read pop "if 1 then 10 else 20" so push 10. Only 10 is left so 10 is the answer. Let's read a complex RPN expression that also has the traditional multiplication operator: 1) 128,8,*,7000,GT,7000,128,8,*,IF eval 128,8,* result is 1024 2) 1024,7000,GT,7000,128,8,*,IF eval 1024,7000,GT result is 0 3) 0,128,8,*,IF eval 128,8,* result is 1024 4) 0,7000,1024,IF result is 1024 Now let's go back to the first example of multiple logic operators but replace the value 20 with the variable "input": 1) input,10,GT,10,input,IF eval is input,10,GT result is A Read eval as "if input > 10 then true" and replace "input,10,GT" with "A: 2) A,10,input,IF eval is A,10,input,IF read "if A then 10 else input". Now replace A it's verbose description and--voila!--you have a easily readable description of the expression: if input > 10 then 10 else input Lastly, let's to back the first most complex example and replace the value 128 with "input": 1) input,8,*,7000,GT,7000,input,8,*,IF eval input,8,* result is A where A is "input * 8" 2) A,7000,GT,7000,input,8,*,IF eval is A,7000,GT result is B where B is "if ((input * 8) > 7000) then true" 3) B,7000,input,8,*,IF eval is input,8,* result is C where C is "input * 8" 4) B,7000,C,IF At last we have a readable decoding of the complex RPN expression with a variable: if ((input * 8) > 7000) then 7000 else (input * 8) =head1 Exercises Exercise 1: Compute "3,2,*,1,+ and "3,2,1,+,*" by hand. Rewrite them in traditional notation. Explain why they have different answers. Answer 1: 3*2+1 = 7 and 3*(2+1) = 9. These expressions have different answers because the altering of the plus and times operators alter the order of their evaluation. Exercise 2: One may be tempted to shorten the expression input,8,*,56000,GT,56000,input,*,8,IF by removing the redundant use of "input,8,*" like so: input,56000,GT,56000,input,IF,8,* Use tradition notation to show these expressions are not the same. Write an expression that's equivalent to the first expression but uses the LE and DIV operators. Answer 2: if (input <= 56000/8 ) { input*8 } else { 56000 } input,56000,8,DIV,LT,input,8,*,56000,IF Exercise 3: Briefly explain why traditional mathematic notation requires the use of parentheses. Explain why RPN notation does not require the use of parentheses. Answer 3: Traditional mathematic expressions are evaluated by doing multiplication and division first, then addition and subtraction. Perentences are used to force the evaluation of addition before multiplication (etc). RPN does not require parentheses because the ordering of objects on the stack can force the evaluation of addition before multiplication. Exercise 4: Explain why it is desirable for the RRDtool developers to implement RPN notation instead of traditional mathematical notation. Answer 4: The algorithm that implements traditional mathematical notation is more complex then algorithm used for RPN. So implementing RPN allowed Tobias Oetiker to write less code! (The code is also less complex and therefore less likely to have bugs.) =head1 AUTHOR steve rader Erader@wiscnet.netE RRDtool/RRDtool-DOC/opt/rrd/doc/rpntutorial.txt010064400000020000002000000244150772067753500212220ustar00binbin00000000000000 RRRRPPPPNNNNTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) 1111....0000....44445555 RRRRPPPPNNNNTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00002222----22226666 NNNNAAAAMMMMEEEE rpntutorial - Reading RRDTtool RPN Expressions by Steve Rader DDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN This tutorial should help you get to grips with rrdtool RPN expressions as seen in CDEF arguments of rrdtool graph. RRRReeeeaaaaddddiiiinnnngggg CCCCoooommmmppppaaaarrrriiiissssoooonnnn OOOOppppeeeerrrraaaattttoooorrrrssss The LT, LE, GT, GE and EQ RPN logic operators are not as tricky as they appear. These operators act on the two values on the stack preceding them (to the left). Read these two values on the stack from left to right inserting the operator in the middle. If the resulting statement is true, the replace the three values from the stack with "1". If the statement if false, replace the three values with "0". For example think about "2,1,GT". This RPN expression could be read as "is two greater than one?" The answer to that question is "true". So the three values should be replaced with "1". Thus the RPN expression 2,1,GT evaluates to 1. Now also consider "2,1,LE". This RPN expression could be read as "is two less than or equal to one?". The natural response is "no" and thus the RPN expression 2,1,LE evaluates to 0. RRRReeeeaaaaddddiiiinnnngggg tttthhhheeee IIIIFFFF OOOOppppeeeerrrraaaattttoooorrrr The IF RPN logic operator can be straightforward also. The key to reading IF operators is to understand that the condition part of the traditional "if X than Y else Z" notation has *already* been evaluated. So the IF operator acts on only one value on the stack: the third value to the left of the IF value. The second value to the left of the IF corresponds to the true ("Y") branch. And the first value to the left of the IF corresponds to the false ("Z") branch. Read the RPN expression "X,Y,Z,IF" from left to right like so: "if X then Y else Z". For example, consider "1,10,100,IF". It looks bizzare to me. But when I read "if 1 then 10 else 100" it's crystal clear: 1 is true so the answer is 10. Note that only zero is false; all other values are true. "2,20,200,IF" ("if 2 then 20 else 200") evaluates to 20. And "0,1,2,IF" ("if 0 then 1 else 2) evaluates to 2. Notice that none of the above examples really simulate the whole "if X then Y else Z" statement. This is because computer programmers read this statement as "if Some Condition then Y else Z". So it's important to be able to read IF operators along with the LT, LE, GT, GE and EQ operators. SSSSoooommmmeeee EEEExxxxaaaammmmpppplllleeeessss While compound expressions can look overly complex, they can be considered elegantly simple. To quickly comprehend RPN expressions, - 1 - Formatted: August 20, 2003 RRRRPPPPNNNNTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) 1111....0000....44445555 RRRRPPPPNNNNTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00002222----22226666 you must know the the algorithm for evaluating RPN expressions: iterate searches from the left to the right looking for an operator, when it's found, apply that operator by popping the operator and some number of values (and by definition, not operators) off the stack. For example, the stack "1,2,3,+,+" gets "2,3,+" evaluated (as "2+3") during the first iteration which is replaced by 5. This results in the stack "1,5,+". Finally, "1,5,+" is evaluated resulting in the answer 6. For convenience sake, it's useful to write this set of operations as: 1) 1,2,3,+,+ eval is 2,3,+ = 5 result is 1,5,+ 2) 1,5,+ eval is 1,5,+ = 6 result is 6 3) 6 Let's use that notation to conviently solve some complex RPN expressions with multiple logic operators: 1) 20,10,GT,10,20,IF eval is 20,10,GT = 1 result is 1,10,20,IF read the eval as pop "20 is greater than 10" so push 1 2) 1,10,20,IF eval is 1,10,20,IF = 10 result is 10 read pop "if 1 then 10 else 20" so push 10. Only 10 is left so 10 is the answer. Let's read a complex RPN expression that also has the traditional multiplication operator: 1) 128,8,*,7000,GT,7000,128,8,*,IF eval 128,8,* result is 1024 2) 1024,7000,GT,7000,128,8,*,IF eval 1024,7000,GT result is 0 3) 0,128,8,*,IF eval 128,8,* result is 1024 4) 0,7000,1024,IF result is 1024 Now let's go back to the first example of multiple logic operators but replace the value 20 with the variable "input": 1) input,10,GT,10,input,IF eval is input,10,GT result is A Read eval as "if input > 10 then true" and replace "input,10,GT" with "A: 2) A,10,input,IF eval is A,10,input,IF read "if A then 10 else input". Now replace A it's verbose description and--voila!--you have a easily readable description of the expression: if input > 10 then 10 else input - 2 - Formatted: August 20, 2003 RRRRPPPPNNNNTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) 1111....0000....44445555 RRRRPPPPNNNNTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00002222----22226666 Lastly, let's to back the first most complex example and replace the value 128 with "input": 1) input,8,*,7000,GT,7000,input,8,*,IF eval input,8,* result is A where A is "input * 8" 2) A,7000,GT,7000,input,8,*,IF eval is A,7000,GT result is B where B is "if ((input * 8) > 7000) then true" 3) B,7000,input,8,*,IF eval is input,8,* result is C where C is "input * 8" 4) B,7000,C,IF At last we have a readable decoding of the complex RPN expression with a variable: if ((input * 8) > 7000) then 7000 else (input * 8) EEEExxxxeeeerrrrcccciiiisssseeeessss Exercise 1: Compute "3,2,*,1,+ and "3,2,1,+,*" by hand. Rewrite them in traditional notation. Explain why they have different answers. Answer 1: 3*2+1 = 7 and 3*(2+1) = 9. These expressions have different answers because the altering of the plus and times operators alter the order of their evaluation. Exercise 2: One may be tempted to shorten the expression input,8,*,56000,GT,56000,input,*,8,IF by removing the redundant use of "input,8,*" like so: input,56000,GT,56000,input,IF,8,* Use tradition notation to show these expressions are not the same. Write an expression that's equivalent to the first expression but uses the LE and DIV operators. Answer 2: - 3 - Formatted: August 20, 2003 RRRRPPPPNNNNTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) 1111....0000....44445555 RRRRPPPPNNNNTTTTUUUUTTTTOOOORRRRIIIIAAAALLLL((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00002222----22226666 if (input <= 56000/8 ) { input*8 } else { 56000 } input,56000,8,DIV,LT,input,8,*,56000,IF Exercise 3: Briefly explain why traditional mathematic notation requires the use of parentheses. Explain why RPN notation does not require the use of parentheses. Answer 3: Traditional mathematic expressions are evaluated by doing multiplication and division first, then addition and subtraction. Perentences are used to force the evaluation of addition before multiplication (etc). RPN does not require parentheses because the ordering of objects on the stack can force the evaluation of addition before multiplication. Exercise 4: Explain why it is desirable for the RRDtool developers to implement RPN notation instead of traditional mathematical notation. Answer 4: The algorithm that implements traditional mathematical notation is more complex then algorithm used for RPN. So implementing RPN allowed Tobias Oetiker to write less code! (The code is also less complex and therefore less likely to have bugs.) AAAAUUUUTTTTHHHHOOOORRRR steve rader - 4 - Formatted: August 20, 2003 RRDtool/RRDtool-DOC/opt/rrd/doc/rrd-beginners.pod010064400000020000002000000347040772067753400213440ustar00binbin00000000000000=head1 NAME rrd-beginners - Beginners guide =head1 SYNOPSIS Helping new RRDTool users to understand the basics of RRDTool =head1 DESCRIPTION This manual is an attempt to assist beginners in understanding the concepts of RRDTool. It sheds a light on differences between RRDTool and other databases. With help of an example, it explains structure of RRDTool database. This is followed by an overview of the "graph" feature of RRDTool. At the end, it has sample scripts that illustrates the usage/wrapping of RRDTool within Shell/Perl. =head2 What makes RRDTool so special? RDtool is GNU licensed software developed by Tobies Oetiker, a system manager at the Swiss Federal Institute of Technology. Though it is a database, there are distinct differences between RRDtool database and other databases as listed below: =over =item * RRDTool stores data; that makes it a back end tool. The RRDtool command set allows the creation of graphs; that makes it a front end tool as well. Other databases just stores data and can not create graphs. =item * In case of linear databases, new data gets appended at the bottom of the database table. Thus its size keeps on increasing, whereas size of an RRDtool database is determined at creation time. Imagine an RRDtool database as the perimeter of a circle. Data is added along the perimeter. When new data reaches the starting point, it overwrites existing data. This way, the size of an RRDtool database always remains constant. The name "Round Robin" stems from this attribute. =item * Other databases store the values as supplied. RRDtool can be configured to calculate the rate of change from the previous to the current value and store this information instead. =item * Other databases get updated when values are supplied. The RRDTool database is structured in such a way that it needs data at predefined time intervals. If it does not get a new value during the interval, it stores an UNKNOWN value for that interval. So, when using the RRDTool database, it is imperative to use scripts that runs at regular intervals to ensure a constant data flow to update the RRDTool database. =back RRDtool has a lot to do with time. With every data update, it also needs to know the time when that update occurred. Time is always expressed in seconds passed since epoch (01-01-1971). RRDTool can be installed on Unix as well as Windows. It has command set to carry out various operations on rrd database. This command set can be accessed from the command line, and from shell or perl scripts. The scripts act as wrappers for accessing data stored in RRDtool database. =head2 Understanding by an example The structure of an RRD database is different than other linear databases. Other databases define tables with columns, and many other parameters. These definitions sometime are very complex, especially in large databases. RRDTool databases are primarily used for monitoring purposes and hence are very simple in structure. The parameters that need to be defined are variables that hold values and archives of those values. Being time sensitive, a couple of time related parameters are also defined. Because of its structure, the definition of an RRDTool database also includes a provision to specify specific actions to take in the absence of update values. Data Source (DS), heartbeat, Date Source Type (DST), Round Robin Archive (RRA), and Consolidation Function (CF) are some of the terminologies related to RRDTool databases. The structure of a database and the terminology associated with it can be best explained with an example. rrdtool create target.rrd --start 1023654125 --step 300 DS:mem:GAUGE:600:0:671744 RRA:AVERAGE:0.5:12:24 RRA:AVERAGE:0.5:288:31 This example creates a database named F. Start time (1023654125) is specified in total number of seconds since epoch (time in seconds since 01-01-1970). While updating the database, update time is also specified. This update time MUST occur after start time and MUST be in seconds since epoch. The step of 300 seconds indicates that database expects new values every 300 seconds. The wrapper script should be scheduled to run every B seconds so that it updates the database every B seconds. DS (Data Source) is the actual variable which relates to the parameter on the device that has to be monitored. Its syntax is DS:variable_name:DST:heartbeat:min:max B is a key word. C is a name under which the parameter is saved in database. There can be as many DSs in a database as needed. After every step interval, a new value of DS is supplied to update the database. This value is also called as Primary Data Point B<(PDP)>. In our example mentioned above, a new PDP is generated every 300 seconds. Note, that if you do NOT supply new datapoints exaclty every 300 seconds, this is not problem, RRDTool will interpolate the data accordingly. B (Data Source Type) defines type of DS. It can be COUNTER, DERIVE, ABSOLUTE, GAUGE. A DS declared as COUNTER will save the rate of change of the value over a step period. This assumes that the value is always increasing (difference between last two values is more than 0). Traffic counters on a router is an ideal candidate for using COUNTER as DST. DERIVE is same as COUNTER but it allows negative values as well. If you want to see the rate of I in free diskspace on your server, then you might want to use the DERIVE data type. ABSOULTE also saves the rate of change but it assumes that previous value is set to 0. The difference between current and previous value is always equal to the current value. So, it stores the current value divided by step interval (300 seconds in our example). GAUGE does not save the rate of change. It saves the actual value itself. There are no divisions/calculations. Memory consumption in a server is an ideal example of gauge. Difference among different types DSTs can be explained better with following example: Values = 300, 600, 900, 1200 Step = 300 seconds COUNTER DS = 1, 1, 1, 1 DERIVE DS = 1, 1, 1, 1 ABSOLUTE DS = 1, 2, 3, 4 GAUGE DS = 300, 600, 900, 1200 The next parameter is B. In our example, heartbeat is 600 seconds. If database doesn not get a new PDP within 300 seconds, it will wait for another 300 seconds (total 600 seconds). If it doesnt receive any PDP with in 600 seconds, it will save an UNKNOWN value into database. This UNKOWN value is a special feature of RRDTool - it is much better than to assume a missing value was 0 (zero). For example, the traffic flow counter on a router keeps on increasing. Lets say, a value is missed for an interval and 0 is stored instead of UNKNOWN. Now when next value becomes available, it will calculate difference between current value and previous value (0) which is not correct. So, inserting value UNKNOWN makes much more sense here. The next two parameters are the minimum and maximum value respectively. If variable to be stored has predictable maximum and minimum value, this should be specified here. Any update value falling out of this range will be saved as UNKNOWN. The next line declares a round robin archive (RRA). The syntax for declaring an RRA is RRA:CF:xff:step:rows RRA is the keyword to declare RRAs. The consolidation function (CF) can be AVERAGE, MINIMUM, MAXIMUM, and LAST. The concept of the consolidated data point (CDP) comes into the picture here. A CDP is CFed (averaged, maximum/minimum value or last value) from I number of PDPs. This RRA will hold I CDPs. Lets have a look at the example above. For the first RRA, 12 (steps) PDPs (DS variables) are AVERAGEed (CF) to form one CDP. 24 (rows) of theses CDPs are archived. Each PDP occurs at 300 seconds. 12 PDPs represent 12 times 300 seconds which is 1 hour. It means 1 CDP (which is equal to 12 PDPs) represents data worth 1 hour. 24 such CDPs represent 1 day (1 hour times 24 CDPs). It means, this RRA is an archive for one day. After 24 CDPs, CDP number 25 will replace the 1st CDP. Second RRA saves 31 CDPs; each CPD represents an AVERAGE value for a day (288 PDPs, each covering 300 seconds = 24 hours). Therefore this RRA is an archive for one month. A single database can have many RRAs. If there are multiple DSs, each individual RRA will save data for all the DSes in the database. For example, if a database has 3 DSs; and daily, weekly, monthly, and yearly RRAs are declared, then each RRA will hold data from all 3 data sources. =head2 Graphical Magic Another important feature of RRDTool is its ability to create graphs. The "graph" command uses "fetch" command internally to retrieve values from the database. With the retrieved values, it draws graphs as defined by the parameters supplied on the command line. A single graph can show different DS (Data Sources0) from a database. It is also possible to show the values from more than one databases into a single graph. Often, it is necessary to perform some math on the values retrieved from database, before plotting them. For example, in SNMP replies, memory consumption values are usually specified in KBytes and traffic flow on interfaces is specified in Bytes. Graphs for these values will be more senseful if values are represented in MBytes and mbps. the RRDTool graph command allows to define such conversions. Apart from mathematical calculations, it is also possible to peform logical operations such as greater than, less than, and if then else. If a database contains more than one RRA archive, then a question may arise - how does RRDTool decide which RRA archive to use for retrieving the values? RRDtool takes looks at several things when making its choice. First it makes sure that the RRA covers as much of the graphing time frame as possible. Second it looks at the resolution of the RRA compared to the resolution of the graph. It tries to find one which has the same or better resolution. With the "-r" option you can force RRDtool to assume a different resolution than the one calculated from the pixel width of the graph. Values of different variables can be presented in 5 different shapes in a graph - AREA, LINE1, LINE2, LINE3, and STACK. AREA is represented by a solid colored area with values as the boundary of this area. LINE1/2/3 (increasing width) are just plain lines representing the values. STACK is also an area but it is "stack"ed on AREA or LINE1/2/3. Another important thing to note, is that variables are plotted in the order they are defined in graph command. So, care must be taken to define STACK only after defining AREA/LINE. It is also possible to put formatted comments within the graph. Detailed instructions be found under graph manual. =head2 Wrapping RRDTool within Shell/Perl script After understanding RRDTool, it is now a time to actually use RRDTool in scripts. Tasks involved in network management are data collection, data storage, and data retrieval. In the following example, the previously created target.rrd database is used. Data collection and data storage is done using Shell scrip. Data retrieval and report generation is done using Perl script. These scripts are as shown below: =head3 Shell script (collects data, updates database) #!/bin/sh a=0 while [ "$a" == 0 ]; do snmpwalk -c public 192.168.1.250 hrSWRunPerfMem > snmp_reply total_mem=`awk 'BEGIN {tot_mem=0} { if ($NF == "KBytes") {tot_mem=tot_mem+$(NF-1)} } END {print tot_mem}' snmp_reply` # I can use N as a replacement for the current time rrdtool update target.rrd N:$total_mem # sleep until the next 300 seconds are full perl -e 'sleep 300 - time % 300' done # end of while loop =head3 Perl script (retrieves data from database and generates graphs and statistics) #!/usr/bin/perl -w #This script fetch data from target.rrd, creates graph of memory consumption on target (Dual P3 Processor 1 GHz, 656 MB RAM) #calling RRD perl module use lib qw( /usr/local/rrdtool-1.0.41/lib/perl ../lib/perl ); use RRDs; my $cur_time = time(); # setting current time my $end_time = $cur_time - 86400; # setting end time to 24 hours behind current time my $start_time = $end_time - 2592000; # setting start time to 30 days from end time #fetching average values from rrd database between start and end time my ($start,$step,$ds_names,$data) = RRDs::fetch("target.rrd", "AVERAGE", "-r", "600", "-s", "$start_time", "-e", "$end_time"); #saving fetched values in 2-dimensional array my $rows = 0; my $columns = 0; my $time_variable = $start; foreach $line (@$data) { $vals[$rows][$columns] = $time_variable; $time_variable = $time_variable + $step; foreach $val (@$line) { $vals[$rows][++$columns] = $val;} $rows++; $columns = 0; } my $tot_time = 0; my $count = 0; #saving values from 2-dimensional into 1-dimensional array for $i ( 0 .. $#vals ) $tot_mem[$count] = $vals[$i][1]; $count++; } my $tot_mem_sum = 0; #calculating total of all values for $i ( 0 .. ($count-1) ) { $tot_mem_sum = $tot_mem_sum + $tot_mem[$i]; } #calculating average of array my $tot_mem_ave = $tot_mem_sum/($count); #creating graph RRDs::graph ("/images/mem_$count.gif", \ "--title= Memory Usage", \ "--vertical-label=Memory Consumption (MB)", \ "--start=$start_time", \ "--end=$end_time", \ "--color=BACK#CCCCCC", \ "--color=CANVAS#CCFFFF", \ "--color=SHADEB#9999CC", \ "--height=125", \ "--upper-limit=656", \ "--lower-limit=0", \ "--rigid", \ "--base=1024", \ "DEF:tot_mem=target.rrd:mem:AVERAGE", \ "CDEF:correct_tot_mem=tot_mem,0,671744,LIMIT,UN,0,tot_mem,IF,1024,/",\ "CDEF:machine_mem=tot_mem,656,+,tot_mem,-",\ "COMMENT:Memory Consumption between $start_time",\ "COMMENT: and $end_time ",\ "HRULE:656#000000:Maximum Available Memory - 656 MB",\ "AREA:machine_mem#CCFFFF:Memory Unused", \ "AREA:correct_tot_mem#6699CC:Total memory consumed in MB"); my $err=RRDs::error; if ($err) {print "problem generating the graph: $err\n";} #printing the output print "Average memory consumption is "; printf "%5.2f",$tot_mem_ave/1024; print " MB. Graphical representation can be found at /images/mem_$count.gif."; =head1 AUTHOR Ketan Patel Ek2pattu@yahoo.comE RRDtool/RRDtool-DOC/opt/rrd/doc/rrd-beginners.txt010064400000020000002000000545720772067753500214070ustar00binbin00000000000000 RRRRRRRRDDDD----BBBBEEEEGGGGIIIINNNNNNNNEEEERRRRSSSS((((1111)))) 1111....0000....44445555 RRRRRRRRDDDD----BBBBEEEEGGGGIIIINNNNNNNNEEEERRRRSSSS((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000003333----00007777----22221111 NNNNAAAAMMMMEEEE rrd-beginners - Beginners guide SSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS Helping new RRDTool users to understand the basics of RRDTool DDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN This manual is an attempt to assist beginners in understanding the concepts of RRDTool. It sheds a light on differences between RRDTool and other databases. With help of an example, it explains structure of RRDTool database. This is followed by an overview of the "graph" feature of RRDTool. At the end, it has sample scripts that illustrates the usage/wrapping of RRDTool within Shell/Perl. WWWWhhhhaaaatttt mmmmaaaakkkkeeeessss RRRRRRRRDDDDTTTToooooooollll ssssoooo ssssppppeeeecccciiiiaaaallll???? RDtool is GNU licensed software developed by Tobies Oetiker, a system manager at the Swiss Federal Institute of Technology. Though it is a database, there are distinct differences between RRDtool database and other databases as listed below: o+ RRDTool stores data; that makes it a back end tool. The RRDtool command set allows the creation of graphs; that makes it a front end tool as well. Other databases just stores data and can not create graphs. o+ In case of linear databases, new data gets appended at the bottom of the database table. Thus its size keeps on increasing, whereas size of an RRDtool database is determined at creation time. Imagine an RRDtool database as the perimeter of a circle. Data is added along the perimeter. When new data reaches the starting point, it overwrites existing data. This way, the size of an RRDtool database always remains constant. The name "Round Robin" stems from this attribute. o+ Other databases store the values as supplied. RRDtool can be configured to calculate the rate of change from the previous to the current value and store this information instead. o+ Other databases get updated when values are supplied. The RRDTool database is structured in such a way that it needs data at predefined time intervals. If it does not get a new value during the interval, it stores an UNKNOWN value for that interval. So, when using the RRDTool database, it is imperative to use scripts that runs at regular intervals to ensure a constant data flow to update the RRDTool database. RRDtool has a lot to do with time. With every data update, it also needs to know the time when that update occurred. Time is always expressed in seconds passed since epoch (01-01-1971). RRDTool can be - 1 - Formatted: August 20, 2003 RRRRRRRRDDDD----BBBBEEEEGGGGIIIINNNNNNNNEEEERRRRSSSS((((1111)))) 1111....0000....44445555 RRRRRRRRDDDD----BBBBEEEEGGGGIIIINNNNNNNNEEEERRRRSSSS((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000003333----00007777----22221111 installed on Unix as well as Windows. It has command set to carry out various operations on rrd database. This command set can be accessed from the command line, and from shell or perl scripts. The scripts act as wrappers for accessing data stored in RRDtool database. UUUUnnnnddddeeeerrrrssssttttaaaannnnddddiiiinnnngggg bbbbyyyy aaaannnn eeeexxxxaaaammmmpppplllleeee The structure of an RRD database is different than other linear databases. Other databases define tables with columns, and many other parameters. These definitions sometime are very complex, especially in large databases. RRDTool databases are primarily used for monitoring purposes and hence are very simple in structure. The parameters that need to be defined are variables that hold values and archives of those values. Being time sensitive, a couple of time related parameters are also defined. Because of its structure, the definition of an RRDTool database also includes a provision to specify specific actions to take in the absence of update values. Data Source (DS), heartbeat, Date Source Type (DST), Round Robin Archive (RRA), and Consolidation Function (CF) are some of the terminologies related to RRDTool databases. The structure of a database and the terminology associated with it can be best explained with an example. rrdtool create target.rrd --start 1023654125 --step 300 DS:mem:GAUGE:600:0:671744 RRA:AVERAGE:0.5:12:24 RRA:AVERAGE:0.5:288:31 This example creates a database named _t_a_r_g_e_t._r_r_d. Start time (1023654125) is specified in total number of seconds since epoch (time in seconds since 01-01-1970). While updating the database, update time is also specified. This update time MUST occur after start time and MUST be in seconds since epoch. The step of 300 seconds indicates that database expects new values every 300 seconds. The wrapper script should be scheduled to run every sssstttteeeepppp seconds so that it updates the database every sssstttteeeepppp seconds. DS (Data Source) is the actual variable which relates to the parameter on the device that has to be monitored. Its syntax is DS:variable_name:DST:heartbeat:min:max DDDDSSSS is a key word. "variable_name" is a name under which the parameter is saved in database. There can be as many DSs in a database as needed. After every step interval, a new value of DS is supplied to update the database. This value is also called as Primary Data Point - 2 - Formatted: August 20, 2003 RRRRRRRRDDDD----BBBBEEEEGGGGIIIINNNNNNNNEEEERRRRSSSS((((1111)))) 1111....0000....44445555 RRRRRRRRDDDD----BBBBEEEEGGGGIIIINNNNNNNNEEEERRRRSSSS((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000003333----00007777----22221111 ((((PPPPDDDDPPPP)))). In our example mentioned above, a new PDP is generated every 300 seconds. Note, that if you do NOT supply new datapoints exaclty every 300 seconds, this is not problem, RRDTool will interpolate the data accordingly. DDDDSSSSTTTT (Data Source Type) defines type of DS. It can be COUNTER, DERIVE, ABSOLUTE, GAUGE. A DS declared as COUNTER will save the rate of change of the value over a step period. This assumes that the value is always increasing (difference between last two values is more than 0). Traffic counters on a router is an ideal candidate for using COUNTER as DST. DERIVE is same as COUNTER but it allows negative values as well. If you want to see the rate of _c_h_a_n_g_e in free diskspace on your server, then you might want to use the DERIVE data type. ABSOULTE also saves the rate of change but it assumes that previous value is set to 0. The difference between current and previous value is always equal to the current value. So, it stores the current value divided by step interval (300 seconds in our example). GAUGE does not save the rate of change. It saves the actual value itself. There are no divisions/calculations. Memory consumption in a server is an ideal example of gauge. Difference among different types DSTs can be explained better with following example: Values = 300, 600, 900, 1200 Step = 300 seconds COUNTER DS = 1, 1, 1, 1 DERIVE DS = 1, 1, 1, 1 ABSOLUTE DS = 1, 2, 3, 4 GAUGE DS = 300, 600, 900, 1200 The next parameter is hhhheeeeaaaarrrrttttbbbbeeeeaaaatttt. In our example, heartbeat is 600 seconds. If database doesn not get a new PDP within 300 seconds, it will wait for another 300 seconds (total 600 seconds). If it doesnt receive any PDP with in 600 seconds, it will save an UNKNOWN value into database. This UNKOWN value is a special feature of RRDTool - it is much better than to assume a missing value was 0 (zero). For example, the traffic flow counter on a router keeps on increasing. Lets say, a value is missed for an interval and 0 is stored instead of UNKNOWN. Now when next value becomes available, it will calculate difference between current value and previous value (0) which is not correct. So, inserting value UNKNOWN makes much more sense here. The next two parameters are the minimum and maximum value respectively. If variable to be stored has predictable maximum and minimum value, this should be specified here. Any update value falling out of this range will be saved as UNKNOWN. The next line declares a round robin archive (RRA). The syntax for declaring an RRA is - 3 - Formatted: August 20, 2003 RRRRRRRRDDDD----BBBBEEEEGGGGIIIINNNNNNNNEEEERRRRSSSS((((1111)))) 1111....0000....44445555 RRRRRRRRDDDD----BBBBEEEEGGGGIIIINNNNNNNNEEEERRRRSSSS((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000003333----00007777----22221111 RRA:CF:xff:step:rows RRA is the keyword to declare RRAs. The consolidation function (CF) can be AVERAGE, MINIMUM, MAXIMUM, and LAST. The concept of the consolidated data point (CDP) comes into the picture here. A CDP is CFed (averaged, maximum/minimum value or last value) from _s_t_e_p number of PDPs. This RRA will hold _r_o_w_s CDPs. Lets have a look at the example above. For the first RRA, 12 (steps) PDPs (DS variables) are AVERAGEed (CF) to form one CDP. 24 (rows) of theses CDPs are archived. Each PDP occurs at 300 seconds. 12 PDPs represent 12 times 300 seconds which is 1 hour. It means 1 CDP (which is equal to 12 PDPs) represents data worth 1 hour. 24 such CDPs represent 1 day (1 hour times 24 CDPs). It means, this RRA is an archive for one day. After 24 CDPs, CDP number 25 will replace the 1st CDP. Second RRA saves 31 CDPs; each CPD represents an AVERAGE value for a day (288 PDPs, each covering 300 seconds = 24 hours). Therefore this RRA is an archive for one month. A single database can have many RRAs. If there are multiple DSs, each individual RRA will save data for all the DSes in the database. For example, if a database has 3 DSs; and daily, weekly, monthly, and yearly RRAs are declared, then each RRA will hold data from all 3 data sources. GGGGrrrraaaapppphhhhiiiiccccaaaallll MMMMaaaaggggiiiicccc Another important feature of RRDTool is its ability to create graphs. The "graph" command uses "fetch" command internally to retrieve values from the database. With the retrieved values, it draws graphs as defined by the parameters supplied on the command line. A single graph can show different DS (Data Sources0) from a database. It is also possible to show the values from more than one databases into a single graph. Often, it is necessary to perform some math on the values retrieved from database, before plotting them. For example, in SNMP replies, memory consumption values are usually specified in KBytes and traffic flow on interfaces is specified in Bytes. Graphs for these values will be more senseful if values are represented in MBytes and mbps. the RRDTool graph command allows to define such conversions. Apart from mathematical calculations, it is also possible to peform logical operations such as greater than, less than, and if then else. If a database contains more than one RRA archive, then a question may arise - how does RRDTool decide which RRA archive to use for retrieving the values? RRDtool takes looks at several things when making its choice. First it makes sure that the RRA covers as much of the graphing time frame as possible. Second it looks at the resolution of the RRA compared to the resolution of the graph. It tries to find one which has the same or better resolution. With the "-r" option you can force RRDtool to assume a different resolution than the one calculated from the pixel width of the graph. Values of different variables can be presented in 5 different shapes - 4 - Formatted: August 20, 2003 RRRRRRRRDDDD----BBBBEEEEGGGGIIIINNNNNNNNEEEERRRRSSSS((((1111)))) 1111....0000....44445555 RRRRRRRRDDDD----BBBBEEEEGGGGIIIINNNNNNNNEEEERRRRSSSS((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000003333----00007777----22221111 in a graph - AREA, LINE1, LINE2, LINE3, and STACK. AREA is represented by a solid colored area with values as the boundary of this area. LINE1/2/3 (increasing width) are just plain lines representing the values. STACK is also an area but it is "stack"ed on AREA or LINE1/2/3. Another important thing to note, is that variables are plotted in the order they are defined in graph command. So, care must be taken to define STACK only after defining AREA/LINE. It is also possible to put formatted comments within the graph. Detailed instructions be found under graph manual. WWWWrrrraaaappppppppiiiinnnngggg RRRRRRRRDDDDTTTToooooooollll wwwwiiiitttthhhhiiiinnnn SSSShhhheeeellllllll////PPPPeeeerrrrllll ssssccccrrrriiiipppptttt After understanding RRDTool, it is now a time to actually use RRDTool in scripts. Tasks involved in network management are data collection, data storage, and data retrieval. In the following example, the previously created target.rrd database is used. Data collection and data storage is done using Shell scrip. Data retrieval and report generation is done using Perl script. These scripts are as shown below: _S_h_e_l_l _s_c_r_i_p_t (_c_o_l_l_e_c_t_s _d_a_t_a, _u_p_d_a_t_e_s _d_a_t_a_b_a_s_e) #!/bin/sh a=0 while [ "$a" == 0 ]; do snmpwalk -c public 192.168.1.250 hrSWRunPerfMem > snmp_reply total_mem=`awk 'BEGIN {tot_mem=0} { if ($NF == "KBytes") {tot_mem=tot_mem+$(NF-1)} } END {print tot_mem}' snmp_reply` # I can use N as a replacement for the current time rrdtool update target.rrd N:$total_mem # sleep until the next 300 seconds are full perl -e 'sleep 300 - time % 300' done # end of while loop _P_e_r_l _s_c_r_i_p_t (_r_e_t_r_i_e_v_e_s _d_a_t_a _f_r_o_m _d_a_t_a_b_a_s_e _a_n_d _g_e_n_e_r_a_t_e_s _g_r_a_p_h_s _a_n_d _s_t_a_t_i_s_t_i_c_s) #!/usr/bin/perl -w #This script fetch data from target.rrd, creates graph of memory consumption on target (Dual P3 Processor 1 GHz, 656 MB RAM) #calling RRD perl module use lib qw( /usr/local/rrdtool-1.0.41/lib/perl ../lib/perl ); use RRDs; my $cur_time = time(); # setting current time my $end_time = $cur_time - 86400; # setting end time to 24 hours behind current time my $start_time = $end_time - 2592000; # setting start time to 30 days from end time - 5 - Formatted: August 20, 2003 RRRRRRRRDDDD----BBBBEEEEGGGGIIIINNNNNNNNEEEERRRRSSSS((((1111)))) 1111....0000....44445555 RRRRRRRRDDDD----BBBBEEEEGGGGIIIINNNNNNNNEEEERRRRSSSS((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000003333----00007777----22221111 - 6 - Formatted: August 20, 2003 RRRRRRRRDDDD----BBBBEEEEGGGGIIIINNNNNNNNEEEERRRRSSSS((((1111)))) 1111....0000....44445555 RRRRRRRRDDDD----BBBBEEEEGGGGIIIINNNNNNNNEEEERRRRSSSS((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000003333----00007777----22221111 #fetching average values from rrd database between start and end time my ($start,$step,$ds_names,$data) = RRDs::fetch("target.rrd", "AVERAGE", "-r", "600", "-s", "$start_time", "-e", "$end_time"); #saving fetched values in 2-dimensional array my $rows = 0; my $columns = 0; my $time_variable = $start; foreach $line (@$data) { $vals[$rows][$columns] = $time_variable; $time_variable = $time_variable + $step; foreach $val (@$line) { $vals[$rows][++$columns] = $val;} $rows++; $columns = 0; } my $tot_time = 0; my $count = 0; #saving values from 2-dimensional into 1-dimensional array for $i ( 0 .. $#vals ) $tot_mem[$count] = $vals[$i][1]; $count++; } my $tot_mem_sum = 0; #calculating total of all values for $i ( 0 .. ($count-1) ) { $tot_mem_sum = $tot_mem_sum + $tot_mem[$i]; } #calculating average of array my $tot_mem_ave = $tot_mem_sum/($count); #creating graph RRDs::graph ("/images/mem_$count.gif", \ "--title= Memory Usage", \ "--vertical-label=Memory Consumption (MB)", \ "--start=$start_time", \ "--end=$end_time", \ "--color=BACK#CCCCCC", \ "--color=CANVAS#CCFFFF", \ "--color=SHADEB#9999CC", \ "--height=125", \ "--upper-limit=656", \ "--lower-limit=0", \ "--rigid", \ "--base=1024", \ "DEF:tot_mem=target.rrd:mem:AVERAGE", \ "CDEF:correct_tot_mem=tot_mem,0,671744,LIMIT,UN,0,tot_mem,IF,1024,/",\ "CDEF:machine_mem=tot_mem,656,+,tot_mem,-",\ "COMMENT:Memory Consumption between $start_time",\ "COMMENT: and $end_time ",\ "HRULE:656#000000:Maximum Available Memory - 656 MB",\ - 7 - Formatted: August 20, 2003 RRRRRRRRDDDD----BBBBEEEEGGGGIIIINNNNNNNNEEEERRRRSSSS((((1111)))) 1111....0000....44445555 RRRRRRRRDDDD----BBBBEEEEGGGGIIIINNNNNNNNEEEERRRRSSSS((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000003333----00007777----22221111 "AREA:machine_mem#CCFFFF:Memory Unused", \ "AREA:correct_tot_mem#6699CC:Total memory consumed in MB"); my $err=RRDs::error; if ($err) {print "problem generating the graph: $err\n";} #printing the output print "Average memory consumption is "; printf "%5.2f",$tot_mem_ave/1024; print " MB. Graphical representation can be found at /images/mem_$count.gif."; AAAAUUUUTTTTHHHHOOOORRRR Ketan Patel - 8 - Formatted: August 20, 2003 RRDtool/RRDtool-DOC/opt/rrd/doc/rrdcgi.pod010064400000020000002000000134730772067753400200550ustar00binbin00000000000000=head1 NAME rrdcgi - create web pages containing RRD graphs based on templates =for html
PDF version.
=head1 SYNOPSIS #!/path/to/B S<[B<--filter>]> =head1 DESCRIPTION B is a sort of very limited script interpreter. Its purpose is to run as a cgi-program and parse a web page template containing special ERRD:: tags. B will interpret and act according to these tags. In the end it will printout a web page including the necessary CGI headers. B parses the contents of the template in 3 steps. In each step it looks only for a subset of tags. This allows to nest tags. The argument parser uses the same semantics as you are used from your c shell. =over 8 =back =head2 Pass 1 =over 8 =item RRD::CV I Inserts the CGI variable of the given name. =item RRD::CV::QUOTE I Inserts the CGI variable of the given name but quotes it, ready for use as an argument in another RRD:: tag. So even when there are spaces in the value of the CGI variable it will still be considered as one argument. =item RRD::CV::PATH I Inserts the CGI variable of the given name, quotes it and makes sure the it starts neither with a '/' nor contains '..'. This is to make sure that no problematic pathnames can be introduced through the CGI interface. =item RRD::GETENV I Get the value of an environment variable. might give you the name of the remote user given you are using some sort of access control on the directory =back =head2 Pass 2 =over 8 =item RRD::GOODFOR I Specify the number of seconds this page should remain valid. This will prompt the rrdcgi to output a Last-Modified, an Expire and if the number of seconds is I a Refresh headers. =item RRD::INCLUDE I Include the contents of the given file into the page returned from the cgi =item RRD::SETENV I I If you want to present your graphs in another time zone than your own, you could use to make sure everything is presented in Universal Time. Note that the values permitted to TZ depend on your OS. =item RRD::TIME::LAST I I This gets replaced by the last modification time of the selected RRD. The time is I-formated with the string specified in the second argument. =item RRD::TIME::NOW I This gets replaced by the current time of day. The time is I-formated with the string specified in the argument. =item RRD::TIME::STRFTIME I I I I This gets replaced by a strftime-formatted time using the format I on either I or I depending on whether I or I is specified. Both I and I must be supplied as either could be relative to the other. This is intended to allow pretty titles on graphs with times that are easier for non rrdtool folks to figure out than "-2weeks". =back =head2 Pass 3 =over 8 =item RRD::GRAPH I This tag creates the RRD graph defined in its argument and then gets replaced by an appropriate EIMGE tag referring to the graph. The B<--lazy> option in RRD graph can be used to make sure that graphs are only regenerated when they are out of date. The arguments to the B tag work as described in the B manual page. Use the B<--lazy> option in your RRD::GRAPH tags, to reduce the load on your server. This option makes sure that graphs are only regenerated when the old ones are out of date. If you do not specify your own B<--imginfo> format, the following will be used: Note that %s stands for the filename part of the graph generated, all directories given in the GIF file argument will get dropped. =item RRD::PRINT I If the preceding B tag contained and B arguments, then you can access their output with this tag. The I argument refers to the number of the B argument. This first B has I 0. =back =head1 EXAMPLE 1 The example below creates a web pages with a single RRD graph. #!/usr/local/bin/rrdcgi RRDCGI Demo

RRDCGI Example Page

=head1 EXAMPLE 2 This script is slightly more elaborate, it allows you to run it from a form which sets RRD_NAME. RRD_NAME is then used to select which RRD you want to use a source for your graph. #!/usr/local/bin/rrdcgi RRDCGI Demo

RRDCGI Example Page for

Selection

Room A, Room B.

Graph

.gif --lazy --title "Temperatures for " DEF:cel=.rrd:exhaust:AVERAGE LINE2:cel#00a000:"D. Celsius">

=head1 EXAMPLE 3 This example shows how to handle the case where the RRD, graphs and cgi-bins are seperate directories #!/.../bin/rrdcgi RRDCGI Demo

RRDCGI test Page

' --lazy --start -1d --end now DEF:http_src=/.../rrds/test.rrd:http_src:AVERAGE AREA:http_src#00ff00:http_src > Note 1: Replace /.../ with the relevant directories Note 2: The SRC=/.../gifs should be paths from the view of the webserver/browser =head1 AUTHOR Tobias Oetiker Eoetiker@ee.ethz.chE RRDtool/RRDtool-DOC/opt/rrd/doc/rrdcgi.txt010064400000020000002000000244230772067753500201100ustar00binbin00000000000000 RRRRRRRRDDDDCCCCGGGGIIII((((1111)))) 1111....0000....44445555 RRRRRRRRDDDDCCCCGGGGIIII((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000003333----00005555----22220000 NNNNAAAAMMMMEEEE rrdcgi - create web pages containing RRD graphs based on templates SSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS #!/path/to/rrrrrrrrddddccccggggiiii [--------ffffiiiilllltttteeeerrrr] DDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN rrrrrrrrddddccccggggiiii is a sort of very limited script interpreter. Its purpose is to run as a cgi-program and parse a web page template containing special might give you the name of the remote user given you are using some sort of access control on the directory PPPPaaaassssssss 2222 RRD::GOODFOR _s_e_c_o_n_d_s Specify the number of seconds this page should remain valid. This will prompt the rrdcgi to output a Last-Modified, an Expire and if the number of seconds is _n_e_g_a_t_i_v_e a Refresh - 1 - Formatted: August 20, 2003 RRRRRRRRDDDDCCCCGGGGIIII((((1111)))) 1111....0000....44445555 RRRRRRRRDDDDCCCCGGGGIIII((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000003333----00005555----22220000 headers. RRD::INCLUDE _f_i_l_e_n_a_m_e Include the contents of the given file into the page returned from the cgi RRD::SETENV _v_a_r_i_a_b_l_e _v_a_l_u_e If you want to present your graphs in another time zone than your own, you could use to make sure everything is presented in Universal Time. Note that the values permitted to TZ depend on your OS. RRD::TIME::LAST _r_r_d-_f_i_l_e _s_t_r_f_t_i_m_e-_f_o_r_m_a_t This gets replaced by the last modification time of the selected RRD. The time is _s_t_r_f_t_i_m_e-formated with the string specified in the second argument. RRD::TIME::NOW _s_t_r_f_t_i_m_e-_f_o_r_m_a_t This gets replaced by the current time of day. The time is _s_t_r_f_t_i_m_e-formated with the string specified in the argument. RRD::TIME::STRFTIME _S_T_A_R_T|_E_N_D _s_t_a_r_t-_s_p_e_c _e_n_d-_s_p_e_c _s_t_r_f_t_i_m_e-_f_o_r_m_a_t This gets replaced by a strftime-formatted time using the format _s_t_r_f_t_i_m_e-_f_o_r_m_a_t on either _s_t_a_r_t-_s_p_e_c or _e_n_d-_s_p_e_c depending on whether _S_T_A_R_T or _E_N_D is specified. Both _s_t_a_r_t- _s_p_e_c and _e_n_d-_s_p_e_c must be supplied as either could be relative to the other. This is intended to allow pretty titles on graphs with times that are easier for non rrdtool folks to figure out than "-2weeks". PPPPaaaassssssss 3333 RRD::GRAPH _r_r_d_g_r_a_p_h _a_r_g_u_m_e_n_t_s This tag creates the RRD graph defined in its argument and then gets replaced by an appropriate tag referring to the graph. The --------llllaaaazzzzyyyy option in RRD graph can be used to make sure that graphs are only regenerated when they are out of date. The arguments to the RRRRRRRRDDDD::::::::GGGGRRRRAAAAPPPPHHHH tag work as described in the rrrrrrrrddddggggrrrraaaapppphhhh manual page. Use the --------llllaaaazzzzyyyy option in your RRD::GRAPH tags, to reduce the load on your server. This option makes sure that graphs are only regenerated when the old ones are out of date. If you do not specify your own --------iiiimmmmggggiiiinnnnffffoooo format, the following will be used: - 2 - Formatted: August 20, 2003 RRRRRRRRDDDDCCCCGGGGIIII((((1111)))) 1111....0000....44445555 RRRRRRRRDDDDCCCCGGGGIIII((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000003333----00005555----22220000 Note that %s stands for the filename part of the graph generated, all directories given in the GIF file argument will get dropped. RRD::PRINT _n_u_m_b_e_r If the preceding RRRRRRRRDDDD::::::::GGGGRRRRAAAAPPPPHHHH tag contained and PPPPRRRRIIIINNNNTTTT arguments, then you can access their output with this tag. The _n_u_m_b_e_r argument refers to the number of the PPPPRRRRIIIINNNNTTTT argument. This first PPPPRRRRIIIINNNNTTTT has _n_u_m_b_e_r 0. EEEEXXXXAAAAMMMMPPPPLLLLEEEE 1111 The example below creates a web pages with a single RRD graph. #!/usr/local/bin/rrdcgi RRDCGI Demo

RRDCGI Example Page

EEEEXXXXAAAAMMMMPPPPLLLLEEEE 2222 This script is slightly more elaborate, it allows you to run it from a form which sets RRD_NAME. RRD_NAME is then used to select which RRD you want to use a source for your graph. #!/usr/local/bin/rrdcgi RRDCGI Demo

RRDCGI Example Page for

Selection

Room A, Room B.

Graph

.gif --lazy --title "Temperatures for " DEF:cel=.rrd:exhaust:AVERAGE LINE2:cel#00a000:"D. Celsius"> - 3 - Formatted: August 20, 2003 RRRRRRRRDDDDCCCCGGGGIIII((((1111)))) 1111....0000....44445555 RRRRRRRRDDDDCCCCGGGGIIII((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000003333----00005555----22220000

EEEEXXXXAAAAMMMMPPPPLLLLEEEE 3333 This example shows how to handle the case where the RRD, graphs and cgi-bins are seperate directories #!/.../bin/rrdcgi RRDCGI Demo

RRDCGI test Page

' --lazy --start -1d --end now DEF:http_src=/.../rrds/test.rrd:http_src:AVERAGE AREA:http_src#00ff00:http_src > Note 1: Replace /.../ with the relevant directories Note 2: The SRC=/.../gifs should be paths from the view of the webserver/browser AAAAUUUUTTTTHHHHOOOORRRR Tobias Oetiker - 4 - Formatted: August 20, 2003 RRDtool/RRDtool-DOC/opt/rrd/doc/rrdcreate.pod010064400000020000002000000232010772067753400205440ustar00binbin00000000000000=head1 NAME rrdtool create - Set up a new Round Robin Database =for html
PDF version.
=head1 SYNOPSIS B B I S<[B<--start>|B<-b> I]> S<[B<--step>|B<-s> I]> S<[BIB<:>IB<:>IB<:>IB<:>I]> S<[BIB<:>IB<:>IB<:>I]> =head1 DESCRIPTION The create function of the RRDtool lets you set up new Round Robin Database (B) files. The file is created at its final, full size and filled with I<*UNKNOWN*> data. =over 8 =item I The name of the B you want to create. B files should end with the extension F<.rrd>. However, B will accept any filename. =item B<--start>|B<-b> I (default: now - 10s) Specifies the time in seconds since 1970-01-01 UTC when the first value should be added to the B. B will not accept any data timed before or at the time specified. See also AT-STYLE TIME SPECIFICATION section in the I documentation for more ways to specify time. =item B<--step>|B<-s> I (default: 300 seconds) Specifies the base interval in seconds with which data will be fed into the B. =item BIB<:>IB<:>IB<:>IB<:>I A single B can accept input from several data sources (B). (e.g. Incoming and Outgoing traffic on a specific communication line). With the B configuration option you must define some basic properties of each data source you want to use to feed the B. I is the name you will use to reference this particular data source from an B. A I must be 1 to 19 characters long in the characters [a-zA-Z0-9_]. I defines the Data Source Type. See the section on "How to Measure" below for further insight. The Datasource Type must be one of the following: =over 4 =item B is for things like temperatures or number of people in a room or value of a RedHat share. =item B is for continuous incrementing counters like the InOctets counter in a router. The B data source assumes that the counter never decreases, except when a counter overflows. The update function takes the overflow into account. The counter is stored as a per-second rate. When the counter overflows, RRDtool checks if the overflow happened at the 32bit or 64bit border and acts accordingly by adding an appropriate value to the result. =item B will store the derivative of the line going from the last to the current value of the data source. This can be useful for gauges, for example, to measure the rate of people entering or leaving a room. Internally, derive works exactly like COUNTER but without overflow checks. So if your counter does not reset at 32 or 64 bit you might want to use DERIVE and combine it with a MIN value of 0. =item B is for counters which get reset upon reading. This is used for fast counters which tend to overflow. So instead of reading them normally you reset them after every read to make sure you have a maximal time available before the next overflow. Another usage is for things you count like number of messages since the last update. =back I defines the maximum number of seconds that may pass between two updates of this data source before the value of the data source is assumed to be I<*UNKNOWN*>. I and I are optional entries defining the expected range of the data supplied by this data source. If I and/or I are defined, any value outside the defined range will be regarded as I<*UNKNOWN*>. If you do not know or care about min and max, set them to U for unknown. Note that min and max always refer to the processed values of the DS. For a traffic-B type DS this would be the max and min data-rate expected from the device. I =item BIB<:>IB<:>IB<:>I The purpose of an B is to store data in the round robin archives (B). An archive consists of a number of data values from all the defined data-sources (B) and is defined with an B line. When data is entered into an B, it is first fit into time slots of the length defined with the B<-s> option becoming a I. The data is also consolidated with the consolidation function (I) of the archive. The following consolidation functions are defined: B, B, B, B. I The xfiles factor defines what part of a consolidation interval may be made up from I<*UNKNOWN*> data while the consolidated value is still regarded as known. I defines how many of these I are used to build a I which then goes into the archive. I defines how many generations of data values are kept in an B. =back =head1 The HEARTBEAT and the STEP Here is an explanation by Don Baarda on the inner workings of rrdtool. It may help you to sort out why all this *UNKNOWN* data is popping up in your databases: RRD gets fed samples at arbitrary times. From these it builds Primary Data Points (PDPs) at exact times every "step" interval. The PDPs are then accumulated into RRAs. The "heartbeat" defines the maximum acceptable interval between samples. If the interval between samples is less than "heartbeat", then an average rate is calculated and applied for that interval. If the interval between samples is longer than "heartbeat", then that entire interval is considered "unknown". Note that there are other things that can make a sample interval "unknown", such as the rate exceeding limits, or even an "unknown" input sample. The known rates during a PDP's "step" interval are used to calculate an average rate for that PDP. Also, if the total "unknown" time during the "step" interval exceeds the "heartbeat", the entire PDP is marked as "unknown". This means that a mixture of known and "unknown" sample time in a single PDP "step" may or may not add up to enough "unknown" time to exceed "heartbeat" and hence mark the whole PDP "unknown". So "heartbeat" is not only the maximum acceptable interval between samples, but also the maximum acceptable amount of "unknown" time per PDP (obviously this is only significant if you have "heartbeat" less than "step"). The "heartbeat" can be short (unusual) or long (typical) relative to the "step" interval between PDPs. A short "heartbeat" means you require multiple samples per PDP, and if you don't get them mark the PDP unknown. A long heartbeat can span multiple "steps", which means it is acceptable to have multiple PDPs calculated from a single sample. An extreme example of this might be a "step" of 5mins and a "heartbeat" of one day, in which case a single sample every day will result in all the PDPs for that entire day period being set to the same average rate. I<-- Don Baarda Edon.baarda@baesystems.comE> =head1 HOW TO MEASURE Here are a few hints on how to measure: =over =item Temperature Normally you have some type of meter you can read to get the temperature. The temperature is not realy connected with a time. The only connection is that the temperature reading happened at a certain time. You can use the B data source type for this. RRRtool will the record your reading together with the time. =item Mail Messages Assume you have a method to count the number of messages transported by your mailserver in a certain amount of time, this give you data like '5 messages in the last 65 seconds'. If you look at the count of 5 like and B datatype you can simply update the rrd with the number 5 and the end time of your monitoring period. RRDtool will then record the number of messages per second. If at some later stage you want to know the number of messages transported in a day, you can get the average messages per second from RRDtool for the day in question and multiply this number with the number of seconds in a day. Because all math is run with Doubles, the precision should be acceptable. =item It's always a Rate RRDtool stores rates in amount/second for COUNTER, DERIVE and ABSOLUTE data. When you plot the data, you will get on the y axis amount/second which you might be tempted to convert to absolute amount volume by multiplying by the delta-time between the points. RRDtool plots continuous data, and as such is not appropriate for plotting absolute volumes as for example "total bytes" sent and received in a router. What you probably want is plot rates that you can scale to for example bytes/hour or plot volumes with another tool that draws bar-plots, where the delta-time is clear on the plot for each point (such that when you read the graph you see for example GB on the y axis, days on the x axis and one bar for each day). =back =head1 EXAMPLE C This sets up an B called F which accepts one temperature value every 300 seconds. If no new data is supplied for more than 600 seconds, the temperature becomes I<*UNKNOWN*>. The minimum acceptable value is -273 and the maximum is 5000. A few archives areas are also defined. The first stores the temperatures supplied for 100 hours (1200 * 300 seconds = 100 hours). The second RRA stores the minimum temperature recorded over every hour (12 * 300 seconds = 1 hour), for 100 days (2400 hours). The third and the fourth RRA's do the same for the maximum and average temperature, respectively. =head1 AUTHOR Tobias Oetiker Eoetiker@ee.ethz.chE RRDtool/RRDtool-DOC/opt/rrd/doc/rrdcreate.txt010064400000020000002000000407020772067753400206060ustar00binbin00000000000000 RRRRRRRRDDDDCCCCRRRREEEEAAAATTTTEEEE((((1111)))) 1111....0000....44445555 RRRRRRRRDDDDCCCCRRRREEEEAAAATTTTEEEE((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----11112222----22220000 NNNNAAAAMMMMEEEE rrdtool create - Set up a new Round Robin Database SSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS rrrrrrrrddddttttoooooooollll ccccrrrreeeeaaaatttteeee _f_i_l_e_n_a_m_e [--------ssssttttaaaarrrrtttt|----bbbb _s_t_a_r_t _t_i_m_e] [--------sssstttteeeepppp|----ssss _s_t_e_p] [DDDDSSSS::::_d_s-_n_a_m_e::::_D_S_T::::_h_e_a_r_t_b_e_a_t::::_m_i_n::::_m_a_x] [RRRRRRRRAAAA::::_C_F::::_x_f_f::::_s_t_e_p_s::::_r_o_w_s] DDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN The create function of the RRDtool lets you set up new Round Robin Database (RRRRRRRRDDDD) files. The file is created at its final, full size and filled with *_U_N_K_N_O_W_N* data. _f_i_l_e_n_a_m_e The name of the RRRRRRRRDDDD you want to create. RRRRRRRRDDDD files should end with the extension ._r_r_d. However, rrrrrrrrddddttttoooooooollll will accept any filename. --------ssssttttaaaarrrrtttt|----bbbb _s_t_a_r_t _t_i_m_e (default: now - 10s) Specifies the time in seconds since 1970-01-01 UTC when the first value should be added to the RRRRRRRRDDDD. rrrrrrrrddddttttoooooooollll will not accept any data timed before or at the time specified. See also AT-STYLE TIME SPECIFICATION section in the _r_r_d_f_e_t_c_h documentation for more ways to specify time. --------sssstttteeeepppp|----ssss _s_t_e_p (default: 300 seconds) Specifies the base interval in seconds with which data will be fed into the RRRRRRRRDDDD. DDDDSSSS::::_d_s-_n_a_m_e::::_D_S_T::::_h_e_a_r_t_b_e_a_t::::_m_i_n::::_m_a_x A single RRRRRRRRDDDD can accept input from several data sources (DDDDSSSS). (e.g. Incoming and Outgoing traffic on a specific communication line). With the DDDDSSSS configuration option you must define some basic properties of each data source you want to use to feed the RRRRRRRRDDDD. _d_s-_n_a_m_e is the name you will use to reference this particular data source from an RRRRRRRRDDDD. A _d_s-_n_a_m_e must be 1 to 19 characters long in the characters [a-zA-Z0-9_]. _D_S_T defines the Data Source Type. See the section on "How to Measure" below for further insight. The Datasource Type must be one of the following: GGGGAAAAUUUUGGGGEEEE is for things like temperatures or number of people in a room or value of a RedHat share. CCCCOOOOUUUUNNNNTTTTEEEERRRR is for continuous incrementing counters like the InOctets - 1 - Formatted: August 20, 2003 RRRRRRRRDDDDCCCCRRRREEEEAAAATTTTEEEE((((1111)))) 1111....0000....44445555 RRRRRRRRDDDDCCCCRRRREEEEAAAATTTTEEEE((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----11112222----22220000 counter in a router. The CCCCOOOOUUUUNNNNTTTTEEEERRRR data source assumes that the counter never decreases, except when a counter overflows. The update function takes the overflow into account. The counter is stored as a per-second rate. When the counter overflows, RRDtool checks if the overflow happened at the 32bit or 64bit border and acts accordingly by adding an appropriate value to the result. DDDDEEEERRRRIIIIVVVVEEEE will store the derivative of the line going from the last to the current value of the data source. This can be useful for gauges, for example, to measure the rate of people entering or leaving a room. Internally, derive works exactly like COUNTER but without overflow checks. So if your counter does not reset at 32 or 64 bit you might want to use DERIVE and combine it with a MIN value of 0. AAAABBBBSSSSOOOOLLLLUUUUTTTTEEEE is for counters which get reset upon reading. This is used for fast counters which tend to overflow. So instead of reading them normally you reset them after every read to make sure you have a maximal time available before the next overflow. Another usage is for things you count like number of messages since the last update. _h_e_a_r_t_b_e_a_t defines the maximum number of seconds that may pass between two updates of this data source before the value of the data source is assumed to be *_U_N_K_N_O_W_N*. _m_i_n and _m_a_x are optional entries defining the expected range of the data supplied by this data source. If _m_i_n and/or _m_a_x are defined, any value outside the defined range will be regarded as *_U_N_K_N_O_W_N*. If you do not know or care about min and max, set them to U for unknown. Note that min and max always refer to the processed values of the DS. For a traffic-CCCCOOOOUUUUNNNNTTTTEEEERRRR type DS this would be the max and min data- rate expected from the device. _I_f _i_n_f_o_r_m_a_t_i_o_n _o_n _m_i_n_i_m_a_l/_m_a_x_i_m_a_l _e_x_p_e_c_t_e_d _v_a_l_u_e_s _i_s _a_v_a_i_l_a_b_l_e, _a_l_w_a_y_s _s_e_t _t_h_e _m_i_n _a_n_d/_o_r _m_a_x _p_r_o_p_e_r_t_i_e_s. _T_h_i_s _w_i_l_l _h_e_l_p _R_R_D_t_o_o_l _i_n _d_o_i_n_g _a _s_i_m_p_l_e _s_a_n_i_t_y _c_h_e_c_k _o_n _t_h_e _d_a_t_a _s_u_p_p_l_i_e_d _w_h_e_n _r_u_n_n_i_n_g _u_p_d_a_t_e. RRRRRRRRAAAA::::_C_F::::_x_f_f::::_s_t_e_p_s::::_r_o_w_s The purpose of an RRRRRRRRDDDD is to store data in the round robin archives (RRRRRRRRAAAA). An archive consists of a number of data values from all the defined data-sources (DDDDSSSS) and is defined with an RRRRRRRRAAAA line. When data is entered into an RRRRRRRRDDDD, it is first fit into time - 2 - Formatted: August 20, 2003 RRRRRRRRDDDDCCCCRRRREEEEAAAATTTTEEEE((((1111)))) 1111....0000....44445555 RRRRRRRRDDDDCCCCRRRREEEEAAAATTTTEEEE((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----11112222----22220000 slots of the length defined with the ----ssss option becoming a _p_r_i_m_a_r_y _d_a_t_a _p_o_i_n_t. The data is also consolidated with the consolidation function (_C_F) of the archive. The following consolidation functions are defined: AAAAVVVVEEEERRRRAAAAGGGGEEEE, MMMMIIIINNNN, MMMMAAAAXXXX, LLLLAAAASSSSTTTT. _x_f_f The xfiles factor defines what part of a consolidation interval may be made up from *_U_N_K_N_O_W_N* data while the consolidated value is still regarded as known. _s_t_e_p_s defines how many of these _p_r_i_m_a_r_y _d_a_t_a _p_o_i_n_t_s are used to build a _c_o_n_s_o_l_i_d_a_t_e_d _d_a_t_a _p_o_i_n_t which then goes into the archive. _r_o_w_s defines how many generations of data values are kept in an RRRRRRRRAAAA. TTTThhhheeee HHHHEEEEAAAARRRRTTTTBBBBEEEEAAAATTTT aaaannnndddd tttthhhheeee SSSSTTTTEEEEPPPP Here is an explanation by Don Baarda on the inner workings of rrdtool. It may help you to sort out why all this *UNKNOWN* data is popping up in your databases: RRD gets fed samples at arbitrary times. From these it builds Primary Data Points (PDPs) at exact times every "step" interval. The PDPs are then accumulated into RRAs. The "heartbeat" defines the maximum acceptable interval between samples. If the interval between samples is less than "heartbeat", then an average rate is calculated and applied for that interval. If the interval between samples is longer than "heartbeat", then that entire interval is considered "unknown". Note that there are other things that can make a sample interval "unknown", such as the rate exceeding limits, or even an "unknown" input sample. The known rates during a PDP's "step" interval are used to calculate an average rate for that PDP. Also, if the total "unknown" time during the "step" interval exceeds the "heartbeat", the entire PDP is marked as "unknown". This means that a mixture of known and "unknown" sample time in a single PDP "step" may or may not add up to enough "unknown" time to exceed "heartbeat" and hence mark the whole PDP "unknown". So "heartbeat" is not only the maximum acceptable interval between samples, but also the maximum acceptable amount of "unknown" time per PDP (obviously this is only significant if you have "heartbeat" less than "step"). The "heartbeat" can be short (unusual) or long (typical) relative to the "step" interval between PDPs. A short "heartbeat" means you require multiple samples per PDP, and if you don't get them mark the PDP unknown. A long heartbeat can span multiple "steps", which means - 3 - Formatted: August 20, 2003 RRRRRRRRDDDDCCCCRRRREEEEAAAATTTTEEEE((((1111)))) 1111....0000....44445555 RRRRRRRRDDDDCCCCRRRREEEEAAAATTTTEEEE((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----11112222----22220000 it is acceptable to have multiple PDPs calculated from a single sample. An extreme example of this might be a "step" of 5mins and a "heartbeat" of one day, in which case a single sample every day will result in all the PDPs for that entire day period being set to the same average rate. -- _D_o_n _B_a_a_r_d_a <_d_o_n._b_a_a_r_d_a@_b_a_e_s_y_s_t_e_m_s._c_o_m> HHHHOOOOWWWW TTTTOOOO MMMMEEEEAAAASSSSUUUURRRREEEE Here are a few hints on how to measure: Temperature Normally you have some type of meter you can read to get the temperature. The temperature is not realy connected with a time. The only connection is that the temperature reading happened at a certain time. You can use the GGGGAAAAUUUUGGGGEEEE data source type for this. RRRtool will the record your reading together with the time. Mail Messages Assume you have a method to count the number of messages transported by your mailserver in a certain amount of time, this give you data like '5 messages in the last 65 seconds'. If you look at the count of 5 like and AAAABBBBSSSSOOOOLLLLUUUUTTTTEEEE datatype you can simply update the rrd with the number 5 and the end time of your monitoring period. RRDtool will then record the number of messages per second. If at some later stage you want to know the number of messages transported in a day, you can get the average messages per second from RRDtool for the day in question and multiply this number with the number of seconds in a day. Because all math is run with Doubles, the precision should be acceptable. It's always a Rate RRDtool stores rates in amount/second for COUNTER, DERIVE and ABSOLUTE data. When you plot the data, you will get on the y axis amount/second which you might be tempted to convert to absolute amount volume by multiplying by the delta-time between the points. RRDtool plots continuous data, and as such is not appropriate for plotting absolute volumes as for example "total bytes" sent and received in a router. What you probably want is plot rates that you can scale to for example bytes/hour or plot volumes with another tool that draws bar-plots, where the delta-time is clear on the plot for each point (such that when you read the graph you see for example GB on the y axis, days on the x axis and one bar for each day). EEEEXXXXAAAAMMMMPPPPLLLLEEEE "rrdtool create temperature.rrd --step 300 DS:temp:GAUGE:600:-273:5000 RRA:AVERAGE:0.5:1:1200 RRA:MIN:0.5:12:2400 RRA:MAX:0.5:12:2400 RRA:AVERAGE:0.5:12:2400" This sets up an RRRRRRRRDDDD called _t_e_m_p_e_r_a_t_u_r_e._r_r_d which accepts one temperature value every 300 seconds. If no new data is supplied for - 4 - Formatted: August 20, 2003 RRRRRRRRDDDDCCCCRRRREEEEAAAATTTTEEEE((((1111)))) 1111....0000....44445555 RRRRRRRRDDDDCCCCRRRREEEEAAAATTTTEEEE((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----11112222----22220000 more than 600 seconds, the temperature becomes *_U_N_K_N_O_W_N*. The minimum acceptable value is -273 and the maximum is 5000. A few archives areas are also defined. The first stores the temperatures supplied for 100 hours (1200 * 300 seconds = 100 hours). The second RRA stores the minimum temperature recorded over every hour (12 * 300 seconds = 1 hour), for 100 days (2400 hours). The third and the fourth RRA's do the same for the maximum and average temperature, respectively. AAAAUUUUTTTTHHHHOOOORRRR Tobias Oetiker - 5 - Formatted: August 20, 2003 RRDtool/RRDtool-DOC/opt/rrd/doc/rrddump.pod010064400000020000002000000012610772067753400202500ustar00binbin00000000000000=head1 NAME rrdtool dump - dump the contents of an B to XML format =for html
PDF version.
=head1 SYNOPSIS B B I E I =head1 DESCRIPTION The B function prints the contents of an B in human readable (?) XML format. This format can be read by rrdrestore. Together they allow you to transfer your files from one architecture to another as well as manipulating the contents of an B file in a somewhat more convenient manner. =over 8 =item I The name of the B you want to dump. =back =head1 AUTHOR Tobias Oetiker Eoetiker@ee.ethz.chE RRDtool/RRDtool-DOC/opt/rrd/doc/rrddump.txt010064400000020000002000000031070772067753500203070ustar00binbin00000000000000 RRRRRRRRDDDDDDDDUUUUMMMMPPPP((((1111)))) 1111....0000....44445555 RRRRRRRRDDDDDDDDUUUUMMMMPPPP((((1111)))) rrrrrrrrddddttttoooooooollll rrrrrrrrddddttttoooooooollll 2222000000002222----00002222----22226666 NNNNAAAAMMMMEEEE rrdtool dump - dump the contents of an RRRRRRRRDDDD to XML format SSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS rrrrrrrrddddttttoooooooollll dddduuuummmmpppp _f_i_l_e_n_a_m_e._r_r_d > _f_i_l_e_n_a_m_e._x_m_l DDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN The dddduuuummmmpppp function prints the contents of an RRRRRRRRDDDD in human readable (?) XML format. This format can be read by rrdrestore. Together they allow you to transfer your files from one architecture to another as well as manipulating the contents of an RRRRRRRRDDDD file in a somewhat more convenient manner. _f_i_l_e_n_a_m_e._r_r_d The name of the RRRRRRRRDDDD you want to dump. AAAAUUUUTTTTHHHHOOOORRRR Tobias Oetiker - 1 - Formatted: August 20, 2003 RRDtool/RRDtool-DOC/opt/rrd/doc/rrdfetch.pod010064400000020000002000000226310772067753400204000ustar00binbin00000000000000=head1 NAME rrdtool fetch - fetch data from an rrd. =for html
PDF version.
=head1 SYNOPSIS B B I I S<[B<--resolution>|B<-r> I]> S<[B<--start>|B<-s> I]> S<[B<--end>|B<-e> I]> =head1 DESCRIPTION The B function is normally used internally by the graph function, to get data from Bs. B will analyze the B and will try to retrieve the data in the resolution requested. The data fetched is printed to stdout. I<*UNKNOWN*> data is often represented by the string "NaN" depending on your OSs printf function. =over 8 =item I the name of the B you want to fetch the data from. =item I which consolidation function should have been applied to the data you want to fetch? (AVERAGE,MIN,MAX,LAST) =item B<--resolution>|B<-r> I (default is the highest resolution) what interval should the values have (seconds per value). B will try to match your request, but it will return data even if no absolute match is possible. B See note below. =item B<--start>|B<-s> I (default end-1day) when should the data begin. A time in seconds since epoch (1970-01-01) is required. Negative numbers are relative to the current time. By default one day worth of data will be fetched. See also AT-STYLE TIME SPECIFICATION section for a detailed explanation on ways to specify start time. =item B<--end>|B<-e> I (default now) when should the data end. Time in seconds since epoch. See also AT-STYLE TIME SPECIFICATION section for a detailed explanation of how to specify end time. =back =head2 RESOLUTION INTERVAL In order to get rrdtool to fetch anything other than the finest resolution RRA B the start and end time must be specified on boundaries that are multiples of the wanted resolution. Consider the following example: rrdtool create subdata.rrd -s 10 DS:ds0:GAUGE:300:0:U \ RRA:AVERAGE:0.5:30:3600 \ RRA:AVERAGE:0.5:90:1200 \ RRA:AVERAGE:0.5:360:1200 \ RRA:MAX:0.5:360:1200 \ RRA:AVERAGE:0.5:8640:600 \ RRA:MAX:0.5:8640:600 This RRD collects data every 10 seconds and stores its averages over 5 minutes, 15 minutes, 1 hour and 1 day as well as the maxima for 1 hour and 1 day. Consider now that you want too fetch the 15 minute average data for last hour. So you might try rrdtool fetch subdata.rrd AVERAGE -r 900 -s -1h However, this will almost always result in a time series that is B in the 15 minute RRA. Therefore the highest resolution RRA, i.e. 5 minute averages, will be chosen which, in this case, is not what you want. Hence, make sure that =over 3 =item 1. both start and end time are a multiple of 900 =item 2. both start and end time are within the wanted RRA =back So, if time now is called "t", do end time == int(t/900)*900, start time == end time -1hour, resolution == 900. In e.g. bash this could look as: TIME=$(date +%s); RRDRES=900; rrdtool fetch subdata.rrd AVERAGE -r $RRDRES \ -e $(echo $(($TIME/$RRDRES*$RRDRES))) -s e-1h Or in perl: perl -e '$ctime = time; $rrdres = 900; system "rrdtool fetch subdata.rrd AVERAGE \ -r $rrdres -e @{[int($ctime/$rrdres)*$rrdres]} -s e-1h"' =head2 AT-STYLE TIME SPECIFICATION Apart from the traditional I, rrdtool does also understand at-style time specification. The specification is called "at-style" after Unix command at(1) that has moderately complex ways to specify time to run your job at. The at-style specification consists of two parts: B