rrdtool-1.4.7/0000777000175300017510000000000011707501542010223 500000000000000rrdtool-1.4.7/bindings/0000777000175300017510000000000011707501552012021 500000000000000rrdtool-1.4.7/bindings/lua/0000777000175300017510000000000011707501553012603 500000000000000rrdtool-1.4.7/bindings/lua/test.lua.bottom0000644000175300017510000000460511077017730015510 00000000000000 local rrd = require 'rrd' local name = 'test.rrd' local start = 300 * math.floor(os.time() / 300) io.write('\n-- Creating ', name, '\n') rrd.create( name, '--start', start-1, '--step', '300', 'DS:a:GAUGE:600:U:U', 'DS:b:GAUGE:600:U:U', 'RRA:AVERAGE:0.5:1:300') local num_points = 0 for t=start, start+300*300, 300 do local s = string.format('%d:%d:%f', t, math.random(100), math.sin(t/800)*50+50) rrd.update(name, s) num_points = num_points + 1 end io.write('rrd file created with ', num_points, ' points, from ', start, ' to ', start+300*300, '\n') io.write('\n-- Testing rrd.info\n') local info = rrd.info(name) for k,v in pairs(info) do io.write(k, '=', v, '\n') end io.write('\n') io.write('-- Testing rrd.fetch\n') io.write('fetching data from ', name, ' - interval: ', start, ' to ', start+300*300, '\n') local fstart, fstep, fnames, fdata = rrd.fetch(name, '--start', start, '--end', start+300*300+10, 'AVERAGE') io.write('got ', table.getn(fdata[1]), ' data sources with ', table.getn(fdata), ' data points each.\n') io.write('\n-- Printing fetched data\n') io.write(' ') for i, n in ipairs(fnames) do io.write(n, ' ') end io.write('\n') for i, v in ipairs(fdata) do local time = fstart + (i-1)*fstep io.write(string.format('%s (%d): ', os.date('%c', time), time)) for _, w in ipairs(v) do io.write(string.format('%e ', w)) end io.write('\n') end io.write('\n') io.write('\n-- Testing rrd.graphv - creates test.png and returns values\n') local t = rrd.graphv( 'test.png', '--title', 'Enjoy Lua RRDtool module!', '--start', start+3600, '--end', 'start + 1000 min', '--interlace', '--imgformat', 'PNG', '--width=450', 'DEF:a=' .. name .. ':a:AVERAGE', 'DEF:b=' .. name .. ':b:AVERAGE', 'CDEF:line=TIME,2400,%,300,LT,a,UNKN,IF', 'AREA:b#00b6e4:beta', 'AREA:line#0022e9:alpha', 'LINE3:line#ff0000', 'VDEF:va=a,AVERAGE', 'VDEF:vb=b,AVERAGE', 'PRINT:va:%5.2lf', 'PRINT:vb:%5.2lf') io.write('\n-- Returned values:\n') io.write('print[0]: ', t['print[0]'], '\n') io.write('print[1]: ', t['print[1]'], '\n') for k, v in pairs(t) do if not string.find(k, '^print%[%d+%]') then io.write(k, ': ', v, '\n') end end io.write('\n') io.write('-- The graph "test.png" was created.\n') io.write('-- Use your preferred viewer to display it.\n\n') rrdtool-1.4.7/bindings/lua/rrdlua.c0000644000175300017510000002063311600551665014161 00000000000000/* * Lua bindings for RRDTool * * This software is licensed to the public under the Free Software * Foundation's GNU GPL, version 2 or later. You may obtain a copy * of the GPL by visiting the Free Software Foundations web site at * www.fsf.org, and a copy is included in this distribution. * * Copyright 2008 Fidelis Assis, all rights reserved. * */ #include #include #include #include #include #include #include #include #include #include #include "lua.h" #include "lauxlib.h" #include "lualib.h" #include "../../src/rrd_tool.h" #ifdef LUA50 #ifdef HAVE_COMPAT51 #include "compat-5.1.h" #else #include "compat-5.1r5/compat-5.1.h" #endif #endif extern void rrd_freemem(void *mem); extern int luaopen_rrd (lua_State * L); typedef int (*RRD_FUNCTION)(int, char **); typedef rrd_info_t *(RRD_FUNCTION_V)(int, char **); /**********************************************************/ static void reset_rrd_state(void) { optind = 0; opterr = 0; rrd_clear_error(); } static char **make_argv(const char *cmd, lua_State * L) { char **argv; int i; int argc = lua_gettop(L) + 1; if (!(argv = calloc(argc, sizeof (char *)))) /* raise an error and never return */ luaL_error(L, "Can't allocate memory for arguments array", cmd); /* fprintf(stderr, "Args:\n"); */ argv[0] = (char *) cmd; /* Dummy arg. Cast to (char *) because rrd */ /* functions don't expect (const * char) */ /* fprintf(stderr, "%s\n", argv[0]); */ for (i=1; ikey); switch (data->type) { case RD_I_CNT: if (isnan(data->value.u_val)) { lua_pushnil(L); } else { lua_pushnumber(L, (lua_Number) data->value.u_val); } lua_rawset(L, -3); break; case RD_I_VAL: lua_pushnumber(L, (lua_Number) data->value.u_val); lua_rawset(L, -3); break; case RD_I_STR: lua_pushstring(L, data->value.u_str); lua_rawset(L, -3); break; case RD_I_BLO: lua_pushlstring(L, (const char *) data->value.u_blo.ptr, data->value.u_blo.size); lua_rawset(L, -3); break; default: rrd_info_free(p); return luaL_error(L, "Wrong data type to info call"); break; } data = data->next; } rrd_info_free(p); return 1; } #endif /**********************************************************/ static int lua_rrd_create (lua_State * L) { rrd_common_call(L, "create", rrd_create); return 0; } static int lua_rrd_dump (lua_State * L) { rrd_common_call(L, "dump", rrd_dump); return 0; } static int lua_rrd_resize (lua_State * L) { rrd_common_call(L, "resize", rrd_resize); return 0; } static int lua_rrd_restore (lua_State * L) { rrd_common_call(L, "restore", rrd_restore); return 0; } static int lua_rrd_tune (lua_State * L) { rrd_common_call(L, "tune", rrd_tune); return 0; } static int lua_rrd_update (lua_State * L) { rrd_common_call(L, "update", rrd_update); return 0; } static int lua_rrd_fetch (lua_State * L) { int argc = lua_gettop(L) + 1; char **argv = make_argv("fetch", L); unsigned long i, j, step, ds_cnt; rrd_value_t *data, *p; char **names; time_t t, start, end; reset_rrd_state(); rrd_fetch(argc, argv, &start, &end, &step, &ds_cnt, &names, &data); free(argv); if (rrd_test_error()) luaL_error(L, rrd_get_error()); lua_pushnumber(L, (lua_Number) start); lua_pushnumber(L, (lua_Number) step); /* fprintf(stderr, "%lu, %lu, %lu, %lu\n", start, end, step, num_points); */ /* create the ds names array */ lua_newtable(L); for (i=0; i test.lua @echo "-- Test script adapted from the one in the Ruby binding." > test.lua @echo >> test.lua @LUA50_TRUE@ @echo "--- compat-5.1.lua is only required for Lua 5.0 ----------" >> test.lua @LUA50_TRUE@@LUA_NEED_OUR_COMPAT51_TRUE@@LUA_SITE_LINSTALL_FALSE@ @echo "original_LUA_PATH = LUA_PATH" >> test.lua @LUA50_TRUE@@LUA_NEED_OUR_COMPAT51_TRUE@@LUA_SITE_LINSTALL_FALSE@ @echo "-- try only compat-5.1.lua installed with RRDtool" >> test.lua @LUA50_TRUE@@LUA_NEED_OUR_COMPAT51_TRUE@@LUA_SITE_LINSTALL_FALSE@ @echo "LUA_PATH = '$(LUA_INSTALL_LMOD)/?.lua'" >> test.lua @LUA50_TRUE@ @echo "local r = pcall(require, 'compat-5.1')" >> test.lua @LUA50_TRUE@ @echo "if not r then" >> test.lua @LUA50_TRUE@ @echo " print('** compat-5.1.lua not found')" >> test.lua @LUA50_TRUE@ @echo " os.exit(1)" >> test.lua @LUA50_TRUE@ @echo "end" >> test.lua @LUA50_TRUE@@LUA_NEED_OUR_COMPAT51_TRUE@@LUA_SITE_LINSTALL_FALSE@ @echo "LUA_PATH = original_LUA_PATH" >> test.lua @LUA50_TRUE@@LUA_NEED_OUR_COMPAT51_TRUE@@LUA_SITE_LINSTALL_FALSE@ @echo "original_LUA_PATH = nil" >> test.lua @LUA50_TRUE@ @echo "----------------------------------------------------------" >> test.lua @LUA50_TRUE@ @echo >> test.lua @LUA_SITE_CINSTALL_FALSE@ @echo "package.cpath = '$(LUA_INSTALL_CMOD)/?.so;' .. package.cpath" >> test.lua @cat test.lua.bottom >> test.lua test: test.lua $(LUA) test.lua # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: rrdtool-1.4.7/bindings/lua/Makefile.am0000644000175300017510000000422211103772756014560 00000000000000# These files are not mentioned in any other Makefile # compat-5.1.lua is only necessary for Lua 5.0 in distros where # it's not already installed. EXTRA_DIST = README test.lua.bottom compat-5.1r5/compat-5.1.lua LIB_VERSION_INFO=0:0:0 LUA = @LUA@ LUA_INSTALL_CMOD = @LUA_INSTALL_CMOD@ LUA_INSTALL_LMOD = @LUA_INSTALL_LMOD@ LUA_DEFINES = @LUA_DEFINES@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LFLAGS = @LUA_LFLAGS@ CLEANFILES = rrd.o rrd.so test.lua test.rrd test.png lualibdir=$(LUA_INSTALL_CMOD) lualib_LTLIBRARIES = rrd.la if LUA_NEED_OUR_COMPAT51 rrd_la_SOURCES = rrdlua.c compat-5.1r5/compat-5.1.c compat-5.1r5/compat-5.1.h compatdir = $(LUA_INSTALL_LMOD) compat_DATA = compat-5.1r5/compat-5.1.lua else rrd_la_SOURCES = rrdlua.c endif rrd_la_LIBADD = -lrrd -lm rrd_la_LDFLAGS = -module -version-info $(LIB_VERSION_INFO) $(LUA_LFLAGS) -L$(top_builddir)/src/.libs rrd_la_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir) $(LUA_CFLAGS) $(LUA_DEFINES) -DLIB_VERSION=\"$(LIB_VERSION)\" .PHONY: test.lua test.lua: test.lua.bottom @echo "-- Created by Makefile." > test.lua @echo "-- Test script adapted from the one in the Ruby binding." > test.lua @echo >> test.lua if LUA50 @echo "--- compat-5.1.lua is only required for Lua 5.0 ----------" >> test.lua if LUA_NEED_OUR_COMPAT51 if !LUA_SITE_LINSTALL @echo "original_LUA_PATH = LUA_PATH" >> test.lua @echo "-- try only compat-5.1.lua installed with RRDtool" >> test.lua @echo "LUA_PATH = '$(LUA_INSTALL_LMOD)/?.lua'" >> test.lua endif endif @echo "local r = pcall(require, 'compat-5.1')" >> test.lua @echo "if not r then" >> test.lua @echo " print('** compat-5.1.lua not found')" >> test.lua @echo " os.exit(1)" >> test.lua @echo "end" >> test.lua if LUA_NEED_OUR_COMPAT51 if !LUA_SITE_LINSTALL @echo "LUA_PATH = original_LUA_PATH" >> test.lua @echo "original_LUA_PATH = nil" >> test.lua endif endif @echo "----------------------------------------------------------" >> test.lua @echo >> test.lua endif if !LUA_SITE_CINSTALL @echo "package.cpath = '$(LUA_INSTALL_CMOD)/?.so;' .. package.cpath" >> test.lua endif @cat test.lua.bottom >> test.lua test: test.lua $(LUA) test.lua rrdtool-1.4.7/bindings/lua/README0000644000175300017510000000457011077017730013404 00000000000000RRDLua is a Lua module for RRD functions. - Configuration From the top dir of RRDtool package, run "./configure", or "./configure --enable-lua-site-install" if you prefer to install in Lua's search path. You should have lua 5.0, or superior, and respective lua-dev packages installed before executing configure. - Compilation and installation Run 'make' and 'sudo make install'. If you don't enable lua-site-install, the Lua modules will be installed together with RRDtool, under the subdir lib/lua/. - Testing Install RRDtool first, as above. Then, enter the bindings/lua dir, run 'make test' and use your preferred viewer to display the just created 'test.png'. If you can read "Enjoy Lua RRDtool module!" on the picture, everything went fine. - Using with Lua 5.1 Start your programs with: ------..----------------------------------------------------------- package.cpath = ' #include #include "lua.h" #include "lauxlib.h" #include "compat-5.1.h" static void getfield(lua_State *L, int idx, const char *name) { const char *end = strchr(name, '.'); lua_pushvalue(L, idx); while (end) { lua_pushlstring(L, name, end - name); lua_gettable(L, -2); lua_remove(L, -2); if (lua_isnil(L, -1)) return; name = end+1; end = strchr(name, '.'); } lua_pushstring(L, name); lua_gettable(L, -2); lua_remove(L, -2); } static void setfield(lua_State *L, int idx, const char *name) { const char *end = strchr(name, '.'); lua_pushvalue(L, idx); while (end) { lua_pushlstring(L, name, end - name); lua_gettable(L, -2); /* create table if not found */ if (lua_isnil(L, -1)) { lua_pop(L, 1); lua_newtable(L); lua_pushlstring(L, name, end - name); lua_pushvalue(L, -2); lua_settable(L, -4); } lua_remove(L, -2); name = end+1; end = strchr(name, '.'); } lua_pushstring(L, name); lua_pushvalue(L, -3); lua_settable(L, -3); lua_pop(L, 2); } LUALIB_API void luaL_module(lua_State *L, const char *libname, const luaL_reg *l, int nup) { if (libname) { getfield(L, LUA_GLOBALSINDEX, libname); /* check whether lib already exists */ if (lua_isnil(L, -1)) { int env, ns; lua_pop(L, 1); /* get rid of nil */ lua_pushliteral(L, "require"); lua_gettable(L, LUA_GLOBALSINDEX); /* look for require */ lua_getfenv(L, -1); /* getfenv(require) */ lua_remove(L, -2); /* remove function require */ env = lua_gettop(L); lua_newtable(L); /* create namespace for lib */ ns = lua_gettop(L); getfield(L, env, "package.loaded"); /* get package.loaded table */ if (lua_isnil(L, -1)) { /* create package.loaded table */ lua_pop(L, 1); /* remove previous result */ lua_newtable(L); lua_pushvalue(L, -1); setfield(L, env, "package.loaded"); } else if (!lua_istable(L, -1)) luaL_error(L, "name conflict for library `%s'", libname); lua_pushstring(L, libname); lua_pushvalue(L, ns); lua_settable(L, -3); /* package.loaded[libname] = ns */ lua_pop(L, 1); /* get rid of package.loaded table */ lua_pushvalue(L, ns); /* copy namespace */ setfield(L, LUA_GLOBALSINDEX, libname); lua_remove (L, env); /* remove env */ } lua_insert(L, -(nup+1)); /* move library table to below upvalues */ } for (; l->name; l++) { int i; lua_pushstring(L, l->name); for (i=0; ifunc, nup); lua_settable(L, -(nup+3)); } lua_pop(L, nup); /* remove upvalues */ } rrdtool-1.4.7/bindings/tcl/0000777000175300017510000000000011707501553012604 500000000000000rrdtool-1.4.7/bindings/tcl/tclrrd.c0000644000175300017510000005322611212751275014165 00000000000000/* * tclrrd.c -- A TCL interpreter extension to access the RRD library. * * Copyright (c) 1999,2000 Frank Strauss, Technical University of Braunschweig. * * Thread-safe code copyright (c) 2005 Oleg Derevenetz, CenterTelecom Voronezh ISP. * * See the file "COPYING" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * $Id: tclrrd.c 1854 2009-06-07 14:46:21Z oetiker $ */ #include #include #include #include #include #include #include "../../src/rrd_tool.h" #include "../../src/rrd_format.h" /* support pre-8.4 tcl */ #ifndef CONST84 # define CONST84 #endif extern int Tclrrd_Init( Tcl_Interp *interp); extern int Tclrrd_SafeInit( Tcl_Interp *interp); /* * some rrd_XXX() and new thread-safe versions of Rrd_XXX() * functions might modify the argv strings passed to it. * Hence, we need to do some preparation before * calling the rrd library functions. */ static char **getopt_init( int argc, CONST84 char *argv[]) { char **argv2; int i; argv2 = calloc(argc, sizeof(char *)); for (i = 0; i < argc; i++) { argv2[i] = strdup(argv[i]); } return argv2; } static void getopt_cleanup( int argc, char **argv2) { int i; for (i = 0; i < argc; i++) { if (argv2[i] != NULL) { free(argv2[i]); } } free(argv2); } static void getopt_free_element( char *argv2[], int argn) { if (argv2[argn] != NULL) { free(argv2[argn]); argv2[argn] = NULL; } } static void getopt_squieeze( int *argc, char *argv2[]) { int i, null_i = 0, argc_tmp = *argc; for (i = 0; i < argc_tmp; i++) { if (argv2[i] == NULL) { (*argc)--; } else { argv2[null_i++] = argv2[i]; } } } /* Thread-safe version */ static int Rrd_Create( ClientData __attribute__((unused)) clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) { int argv_i; char **argv2; char *parsetime_error = NULL; time_t last_up = time(NULL) - 10; long int long_tmp; unsigned long int pdp_step = 300; rrd_time_value_t last_up_tv; argv2 = getopt_init(argc, argv); for (argv_i = 1; argv_i < argc; argv_i++) { if (!strcmp(argv2[argv_i], "--start") || !strcmp(argv2[argv_i], "-b")) { if (argv_i++ >= argc) { Tcl_AppendResult(interp, "RRD Error: option '", argv2[argv_i - 1], "' needs an argument", (char *) NULL); getopt_cleanup(argc, argv2); return TCL_ERROR; } if ((parsetime_error = rrd_parsetime(argv2[argv_i], &last_up_tv))) { Tcl_AppendResult(interp, "RRD Error: invalid time format: '", argv2[argv_i], "'", (char *) NULL); getopt_cleanup(argc, argv2); return TCL_ERROR; } if (last_up_tv.type == RELATIVE_TO_END_TIME || last_up_tv.type == RELATIVE_TO_START_TIME) { Tcl_AppendResult(interp, "RRD Error: specifying time relative to the 'start' ", "or 'end' makes no sense here", (char *) NULL); getopt_cleanup(argc, argv2); return TCL_ERROR; } last_up = mktime(&last_up_tv.tm) +last_up_tv.offset; if (last_up < 3600 * 24 * 365 * 10) { Tcl_AppendResult(interp, "RRD Error: the first entry to the RRD should be after 1980", (char *) NULL); getopt_cleanup(argc, argv2); return TCL_ERROR; } getopt_free_element(argv2, argv_i - 1); getopt_free_element(argv2, argv_i); } else if (!strcmp(argv2[argv_i], "--step") || !strcmp(argv2[argv_i], "-s")) { if (argv_i++ >= argc) { Tcl_AppendResult(interp, "RRD Error: option '", argv2[argv_i - 1], "' needs an argument", (char *) NULL); getopt_cleanup(argc, argv2); return TCL_ERROR; } long_tmp = atol(argv2[argv_i]); if (long_tmp < 1) { Tcl_AppendResult(interp, "RRD Error: step size should be no less than one second", (char *) NULL); getopt_cleanup(argc, argv2); return TCL_ERROR; } pdp_step = long_tmp; getopt_free_element(argv2, argv_i - 1); getopt_free_element(argv2, argv_i); } else if (!strcmp(argv2[argv_i], "--")) { getopt_free_element(argv2, argv_i); break; } else if (argv2[argv_i][0] == '-') { Tcl_AppendResult(interp, "RRD Error: unknown option '", argv2[argv_i], "'", (char *) NULL); getopt_cleanup(argc, argv2); return TCL_ERROR; } } getopt_squieeze(&argc, argv2); if (argc < 2) { Tcl_AppendResult(interp, "RRD Error: needs rrd filename", (char *) NULL); getopt_cleanup(argc, argv2); return TCL_ERROR; } rrd_create_r(argv2[1], pdp_step, last_up, argc - 2, (const char **)argv2 + 2); getopt_cleanup(argc, argv2); if (rrd_test_error()) { Tcl_AppendResult(interp, "RRD Error: ", rrd_get_error(), (char *) NULL); rrd_clear_error(); return TCL_ERROR; } return TCL_OK; } /* Thread-safe version */ static int Rrd_Dump( ClientData __attribute__((unused)) clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) { if (argc < 2) { Tcl_AppendResult(interp, "RRD Error: needs rrd filename", (char *) NULL); return TCL_ERROR; } rrd_dump_r(argv[1], NULL); /* NOTE: rrd_dump() writes to stdout. No interaction with TCL. */ if (rrd_test_error()) { Tcl_AppendResult(interp, "RRD Error: ", rrd_get_error(), (char *) NULL); rrd_clear_error(); return TCL_ERROR; } return TCL_OK; } /* Thread-safe version */ static int Rrd_Flushcached( ClientData __attribute__((unused)) clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) { if (argc < 2) { Tcl_AppendResult(interp, "RRD Error: needs rrd filename", (char *) NULL); return TCL_ERROR; } rrd_flushcached(argc, (char**)argv); if (rrd_test_error()) { Tcl_AppendResult(interp, "RRD Error: ", rrd_get_error(), (char *) NULL); rrd_clear_error(); return TCL_ERROR; } return TCL_OK; } /* Thread-safe version */ static int Rrd_Last( ClientData __attribute__((unused)) clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) { time_t t; if (argc < 2) { Tcl_AppendResult(interp, "RRD Error: needs rrd filename", (char *) NULL); return TCL_ERROR; } t = rrd_last_r(argv[1]); if (rrd_test_error()) { Tcl_AppendResult(interp, "RRD Error: ", rrd_get_error(), (char *) NULL); rrd_clear_error(); return TCL_ERROR; } Tcl_SetIntObj(Tcl_GetObjResult(interp), t); return TCL_OK; } /* Thread-safe version */ static int Rrd_Update( ClientData __attribute__((unused)) clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) { int argv_i; char **argv2, *template = NULL; argv2 = getopt_init(argc, argv); for (argv_i = 1; argv_i < argc; argv_i++) { if (!strcmp(argv2[argv_i], "--template") || !strcmp(argv2[argv_i], "-t")) { if (argv_i++ >= argc) { Tcl_AppendResult(interp, "RRD Error: option '", argv2[argv_i - 1], "' needs an argument", (char *) NULL); if (template != NULL) { free(template); } getopt_cleanup(argc, argv2); return TCL_ERROR; } if (template != NULL) { free(template); } template = strdup(argv2[argv_i]); getopt_free_element(argv2, argv_i - 1); getopt_free_element(argv2, argv_i); } else if (!strcmp(argv2[argv_i], "--")) { getopt_free_element(argv2, argv_i); break; } else if (argv2[argv_i][0] == '-') { Tcl_AppendResult(interp, "RRD Error: unknown option '", argv2[argv_i], "'", (char *) NULL); if (template != NULL) { free(template); } getopt_cleanup(argc, argv2); return TCL_ERROR; } } getopt_squieeze(&argc, argv2); if (argc < 2) { Tcl_AppendResult(interp, "RRD Error: needs rrd filename", (char *) NULL); if (template != NULL) { free(template); } getopt_cleanup(argc, argv2); return TCL_ERROR; } rrd_update_r(argv2[1], template, argc - 2, (const char **)argv2 + 2); if (template != NULL) { free(template); } getopt_cleanup(argc, argv2); if (rrd_test_error()) { Tcl_AppendResult(interp, "RRD Error: ", rrd_get_error(), (char *) NULL); rrd_clear_error(); return TCL_ERROR; } return TCL_OK; } static int Rrd_Lastupdate( ClientData __attribute__((unused)) clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) { time_t last_update; char **argv2; char **ds_namv; char **last_ds; char s[30]; Tcl_Obj *listPtr; unsigned long ds_cnt, i; /* TODO: support for rrdcached */ if (argc != 2) { Tcl_AppendResult(interp, "RRD Error: needs a single rrd filename", (char *) NULL); return TCL_ERROR; } argv2 = getopt_init(argc, argv); if (rrd_lastupdate_r(argv2[1], &last_update, &ds_cnt, &ds_namv, &last_ds) == 0) { listPtr = Tcl_GetObjResult(interp); for (i = 0; i < ds_cnt; i++) { sprintf(s, " %28s", ds_namv[i]); Tcl_ListObjAppendElement(interp, listPtr, Tcl_NewStringObj(s, -1)); sprintf(s, "\n\n%10lu:", last_update); Tcl_ListObjAppendElement(interp, listPtr, Tcl_NewStringObj(s, -1)); for (i = 0; i < ds_cnt; i++) { sprintf(s, " %s", last_ds[i]); Tcl_ListObjAppendElement(interp, listPtr, Tcl_NewStringObj(s, -1)); free(last_ds[i]); free(ds_namv[i]); } sprintf(s, "\n"); Tcl_ListObjAppendElement(interp, listPtr, Tcl_NewStringObj(s, -1)); free(last_ds); free(ds_namv); } } return TCL_OK; } static int Rrd_Fetch( ClientData __attribute__((unused)) clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) { time_t start, end, j; unsigned long step, ds_cnt, i, ii; rrd_value_t *data, *datai; char **ds_namv; Tcl_Obj *listPtr; char s[30]; char **argv2; argv2 = getopt_init(argc, argv); if (rrd_fetch(argc, argv2, &start, &end, &step, &ds_cnt, &ds_namv, &data) != -1) { datai = data; listPtr = Tcl_GetObjResult(interp); for (j = start; j <= end; j += step) { for (ii = 0; ii < ds_cnt; ii++) { sprintf(s, "%.2f", *(datai++)); Tcl_ListObjAppendElement(interp, listPtr, Tcl_NewStringObj(s, -1)); } } for (i = 0; i < ds_cnt; i++) free(ds_namv[i]); free(ds_namv); free(data); } getopt_cleanup(argc, argv2); if (rrd_test_error()) { Tcl_AppendResult(interp, "RRD Error: ", rrd_get_error(), (char *) NULL); rrd_clear_error(); return TCL_ERROR; } return TCL_OK; } static int Rrd_Graph( ClientData __attribute__((unused)) clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) { Tcl_Channel channel; int mode, fd2; ClientData fd1; FILE *stream = NULL; char **calcpr = NULL; int rc, xsize, ysize; double ymin, ymax; char dimensions[50]; char **argv2; CONST84 char *save; /* * If the "filename" is a Tcl fileID, then arrange for rrd_graph() to write to * that file descriptor. Will this work with windoze? I have no idea. */ if ((channel = Tcl_GetChannel(interp, argv[1], &mode)) != NULL) { /* * It >is< a Tcl fileID */ if (!(mode & TCL_WRITABLE)) { Tcl_AppendResult(interp, "channel \"", argv[1], "\" wasn't opened for writing", (char *) NULL); return TCL_ERROR; } /* * Must flush channel to make sure any buffered data is written before * rrd_graph() writes to the stream */ if (Tcl_Flush(channel) != TCL_OK) { Tcl_AppendResult(interp, "flush failed for \"", argv[1], "\": ", strerror(Tcl_GetErrno()), (char *) NULL); return TCL_ERROR; } if (Tcl_GetChannelHandle(channel, TCL_WRITABLE, &fd1) != TCL_OK) { Tcl_AppendResult(interp, "cannot get file descriptor associated with \"", argv[1], "\"", (char *) NULL); return TCL_ERROR; } /* * Must dup() file descriptor so we can fclose(stream), otherwise the fclose() * would close Tcl's file descriptor */ if ((fd2 = dup((int)fd1)) == -1) { Tcl_AppendResult(interp, "dup() failed for file descriptor associated with \"", argv[1], "\": ", strerror(errno), (char *) NULL); return TCL_ERROR; } /* * rrd_graph() wants a FILE* */ if ((stream = fdopen(fd2, "wb")) == NULL) { Tcl_AppendResult(interp, "fdopen() failed for file descriptor associated with \"", argv[1], "\": ", strerror(errno), (char *) NULL); close(fd2); /* plug potential file descriptor leak */ return TCL_ERROR; } save = argv[1]; argv[1] = "-"; argv2 = getopt_init(argc, argv); argv[1] = save; } else { Tcl_ResetResult(interp); /* clear error from Tcl_GetChannel() */ argv2 = getopt_init(argc, argv); } rc = rrd_graph(argc, argv2, &calcpr, &xsize, &ysize, stream, &ymin, &ymax); getopt_cleanup(argc, argv2); if (stream != NULL) fclose(stream); /* plug potential malloc & file descriptor leak */ if (rc != -1) { sprintf(dimensions, "%d %d", xsize, ysize); Tcl_AppendResult(interp, dimensions, (char *) NULL); if (calcpr) { #if 0 int i; for (i = 0; calcpr[i]; i++) { printf("%s\n", calcpr[i]); free(calcpr[i]); } #endif free(calcpr); } } if (rrd_test_error()) { Tcl_AppendResult(interp, "RRD Error: ", rrd_get_error(), (char *) NULL); rrd_clear_error(); return TCL_ERROR; } return TCL_OK; } static int Rrd_Tune( ClientData __attribute__((unused)) clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) { char **argv2; argv2 = getopt_init(argc, argv); rrd_tune(argc, argv2); getopt_cleanup(argc, argv2); if (rrd_test_error()) { Tcl_AppendResult(interp, "RRD Error: ", rrd_get_error(), (char *) NULL); rrd_clear_error(); return TCL_ERROR; } return TCL_OK; } static int Rrd_Resize( ClientData __attribute__((unused)) clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) { char **argv2; argv2 = getopt_init(argc, argv); rrd_resize(argc, argv2); getopt_cleanup(argc, argv2); if (rrd_test_error()) { Tcl_AppendResult(interp, "RRD Error: ", rrd_get_error(), (char *) NULL); rrd_clear_error(); return TCL_ERROR; } return TCL_OK; } static int Rrd_Restore( ClientData __attribute__((unused)) clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) { char **argv2; argv2 = getopt_init(argc, argv); rrd_restore(argc, argv2); getopt_cleanup(argc, argv2); if (rrd_test_error()) { Tcl_AppendResult(interp, "RRD Error: ", rrd_get_error(), (char *) NULL); rrd_clear_error(); return TCL_ERROR; } return TCL_OK; } /* * The following structure defines the commands in the Rrd extension. */ typedef struct { char *name; /* Name of the command. */ Tcl_CmdProc *proc; /* Procedure for command. */ int hide; /* Hide if safe interpreter */ } CmdInfo; static CmdInfo rrdCmds[] = { {"Rrd::create", Rrd_Create, 1}, /* Thread-safe version */ {"Rrd::dump", Rrd_Dump, 0}, /* Thread-safe version */ {"Rrd::flushcached", Rrd_Flushcached, 0}, {"Rrd::last", Rrd_Last, 0}, /* Thread-safe version */ {"Rrd::lastupdate", Rrd_Lastupdate, 0}, /* Thread-safe version */ {"Rrd::update", Rrd_Update, 1}, /* Thread-safe version */ {"Rrd::fetch", Rrd_Fetch, 0}, {"Rrd::graph", Rrd_Graph, 1}, /* Due to RRD's API, a safe interpreter cannot create a graph since it writes to a filename supplied by the caller */ {"Rrd::tune", Rrd_Tune, 1}, {"Rrd::resize", Rrd_Resize, 1}, {"Rrd::restore", Rrd_Restore, 1}, {(char *) NULL, (Tcl_CmdProc *) NULL, 0} }; static int init( Tcl_Interp *interp, int safe) { CmdInfo *cmdInfoPtr; Tcl_CmdInfo info; if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) return TCL_ERROR; if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 1) == NULL) { return TCL_ERROR; } /* * Why a global array? In keeping with the Rrd:: namespace, why * not simply create a normal variable Rrd::version and set it? */ Tcl_SetVar2(interp, "rrd", "version", VERSION, TCL_GLOBAL_ONLY); for (cmdInfoPtr = rrdCmds; cmdInfoPtr->name != NULL; cmdInfoPtr++) { /* * Check if the command already exists and return an error * to ensure we detect name clashes while loading the Rrd * extension. */ if (Tcl_GetCommandInfo(interp, cmdInfoPtr->name, &info)) { Tcl_AppendResult(interp, "command \"", cmdInfoPtr->name, "\" already exists", (char *) NULL); return TCL_ERROR; } if (safe && cmdInfoPtr->hide) { #if 0 /* * Turns out the one cannot hide a command in a namespace * due to a limitation of Tcl, one can only hide global * commands. Thus, if we created the commands without * the Rrd:: namespace in a safe interpreter, then the * "unsafe" commands could be hidden -- which would allow * an owning interpreter either un-hiding them or doing * an "interp invokehidden". If the Rrd:: namespace is * used, then it's still possible for the owning interpreter * to fake out the missing commands: * * # Make all Rrd::* commands available in master interperter * package require Rrd * set safe [interp create -safe] * # Make safe Rrd::* commands available in safe interperter * interp invokehidden $safe -global load ./tclrrd1.2.11.so * # Provide the safe interpreter with the missing commands * $safe alias Rrd::update do_update $safe * proc do_update {which_interp $args} { * # Do some checking maybe... * : * return [eval Rrd::update $args] * } * * Our solution for now is to just not create the "unsafe" * commands in a safe interpreter. */ if (Tcl_HideCommand(interp, cmdInfoPtr->name, cmdInfoPtr->name) != TCL_OK) return TCL_ERROR; #endif } else Tcl_CreateCommand(interp, cmdInfoPtr->name, cmdInfoPtr->proc, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); } if (Tcl_PkgProvide(interp, "Rrd", VERSION) != TCL_OK) { return TCL_ERROR; } return TCL_OK; } int Tclrrd_Init( Tcl_Interp *interp) { return init(interp, 0); } /* * See the comments above and note how few commands are considered "safe"... * Using rrdtool in a safe interpreter has very limited functionality. It's * tempting to just return TCL_ERROR and forget about it. */ int Tclrrd_SafeInit( Tcl_Interp *interp) { return init(interp, 1); } rrdtool-1.4.7/bindings/tcl/ifOctets.tcl.in0000644000175300017510000000244610766723564015433 00000000000000#!/bin/sh # the next line restarts using tclsh -*- tcl -*- \ exec tclsh@TCL_VERSION@ "$0" "$@" #package require Tnm 3.0 package require Rrd @VERSION@ set rrdfile "[lindex $argv 0]-[lindex $argv 1].rrd" # create rrdfile if not yet existent if {[file exists $rrdfile] == 0} { Rrd::create $rrdfile --step 5 \ DS:inOctets:COUNTER:10:U:U DS:outOctets:COUNTER:10:U:U \ RRA:AVERAGE:0.5:1:12 } # get an snmp session context set session [Tnm::snmp generator -address [lindex $argv 0]] # walk through the ifDescr column to find the right interface $session walk descr IF-MIB!ifDescr { # is this the right interface? if {"[Tnm::snmp value $descr 0]" == "[lindex $argv 1]"} { # get the instance part of this table row set inst [lindex [Tnm::mib split [Tnm::snmp oid $descr 0]] 1] # get the two interface's octet counter values set in [lindex [lindex [$session get IF-MIB!ifInOctets.$inst] 0] 2] set out [lindex [lindex [$session get IF-MIB!ifOutOctets.$inst] 0] 2] # write the values to the rrd puts "$in $out" Rrd::update $rrdfile --template inOctets:outOctets N:$in:$out Rrd::graph gaga.png --title "gaga" \ DEF:in=$rrdfile:inOctets:AVERAGE \ DEF:out=$rrdfile:outOctets:AVERAGE \ AREA:in#0000FF:inOctets \ LINE2:out#00C000:outOctets #puts [Rrd::fetch $rrdfile AVERAGE] } } rrdtool-1.4.7/bindings/tcl/Makefile.in0000644000175300017510000005217511707501506014575 00000000000000# Makefile.in generated by automake 1.11 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = bindings/tcl DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ $(srcdir)/ifOctets.tcl.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/acinclude.m4 \ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \ $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/rrd_config.h CONFIG_CLEAN_FILES = ifOctets.tcl CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkglibdir)" \ "$(DESTDIR)$(tclpkgdir)" "$(DESTDIR)$(pkglibdir)" \ "$(DESTDIR)$(tclpkgdir)" LIBRARIES = $(lib_LIBRARIES) SCRIPTS = $(pkglib_SCRIPTS) $(tclpkg_SCRIPTS) AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ SOURCES = DIST_SOURCES = DATA = $(pkglib_DATA) $(tclpkg_DATA) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ALL_LIBS = @ALL_LIBS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ COMP_LUA = @COMP_LUA@ COMP_PERL = @COMP_PERL@ COMP_PYTHON = @COMP_PYTHON@ COMP_RUBY = @COMP_RUBY@ CORE_LIBS = @CORE_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ GMSGFMT = @GMSGFMT@ GMSGFMT_015 = @GMSGFMT_015@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTLLIBS = @INTLLIBS@ INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBVERS = @LIBVERS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ LUA = @LUA@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_DEFINES = @LUA_DEFINES@ LUA_INSTALL_CMOD = @LUA_INSTALL_CMOD@ LUA_INSTALL_LMOD = @LUA_INSTALL_LMOD@ LUA_LFLAGS = @LUA_LFLAGS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ MSGFMT = @MSGFMT@ MSGFMT_015 = @MSGFMT_015@ MSGMERGE = @MSGMERGE@ MULTITHREAD_CFLAGS = @MULTITHREAD_CFLAGS@ MULTITHREAD_LDFLAGS = @MULTITHREAD_LDFLAGS@ NM = @NM@ NMEDIT = @NMEDIT@ NROFF = @NROFF@ NUMVERS = @NUMVERS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PERL = @PERL@ PERLCC = @PERLCC@ PERLCCFLAGS = @PERLCCFLAGS@ PERLFLAGS = @PERLFLAGS@ PERLLD = @PERLLD@ PERLLDFLAGS = @PERLLDFLAGS@ PERL_CC = @PERL_CC@ PERL_MAKE_OPTIONS = @PERL_MAKE_OPTIONS@ PERL_VERSION = @PERL_VERSION@ PKGCONFIG = @PKGCONFIG@ POD2HTML = @POD2HTML@ POD2MAN = @POD2MAN@ POSUB = @POSUB@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_INCLUDES = @PYTHON_INCLUDES@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ RRDDOCDIR = @RRDDOCDIR@ RRDGRAPH_YLEGEND_ANGLE = @RRDGRAPH_YLEGEND_ANGLE@ RRD_DEFAULT_FONT = @RRD_DEFAULT_FONT@ RRD_GETOPT_LONG = @RRD_GETOPT_LONG@ RUBY = @RUBY@ RUBY_MAKE_OPTIONS = @RUBY_MAKE_OPTIONS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ TCL_INCLUDE_SPEC = @TCL_INCLUDE_SPEC@ TCL_LD_SEARCH_FLAGS = @TCL_LD_SEARCH_FLAGS@ TCL_PACKAGE_DIR = @TCL_PACKAGE_DIR@ TCL_PACKAGE_PATH = @TCL_PACKAGE_PATH@ ### no including this by default @WERROR@ TCL_PREFIX = @TCL_PREFIX@ TCL_SHLIB_CFLAGS = @TCL_SHLIB_CFLAGS@ TCL_SHLIB_LD = @TCL_SHLIB_LD@ TCL_SHLIB_SUFFIX = @TCL_SHLIB_SUFFIX@ TCL_STUB_LIB_SPEC = @TCL_STUB_LIB_SPEC@ TCL_VERSION = @TCL_VERSION@ TROFF = @TROFF@ USE_NLS = @USE_NLS@ VERSION = @VERSION@ WERROR = @WERROR@ XGETTEXT = @XGETTEXT@ XGETTEXT_015 = @XGETTEXT_015@ XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ acx_pthread_config = @acx_pthread_config@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ EXTRA_DIST = README tclrrd.c AM_CFLAGS = @CFLAGS@ CLEANFILES = tclrrd.o tclrrd.so SRC_DIR = $(top_srcdir)/src AM_CPPFLAGS = $(TCL_INCLUDE_SPEC) -I$(SRC_DIR) -DUSE_TCL_STUBS LIBDIRS = -L$(top_builddir)/src/.libs -L$(top_builddir)/src -L$(libdir) LIB_RUNTIME_DIR = $(libdir) @BUILD_TCL_SITE_TRUE@tclpkgdir = @TCL_PACKAGE_DIR@ @BUILD_TCL_SITE_TRUE@tclpkg_DATA = pkgIndex.tcl @BUILD_TCL_SITE_TRUE@tclpkg_SCRIPTS = ifOctets.tcl @BUILD_TCL_SITE_FALSE@pkglib_DATA = pkgIndex.tcl @BUILD_TCL_SITE_FALSE@pkglib_SCRIPTS = ifOctets.tcl # Automake doen't like `tclrrd$(VERSION)$(TCL_SHLIB_SUFFIX)' as # library name. So we build and install this library `by hand'. # # We do, however, specify a lib_LIBRARIES target such that # automake creates the directory (if neecessary). # TCL_RRD_LIB = tclrrd$(VERSION)$(TCL_SHLIB_SUFFIX) lib_LIBRARIES = all: all-am .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu bindings/tcl/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu bindings/tcl/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): ifOctets.tcl: $(top_builddir)/config.status $(srcdir)/ifOctets.tcl.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ install-libLIBRARIES: $(lib_LIBRARIES) @$(NORMAL_INSTALL) test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" @list='$(lib_LIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(INSTALL_DATA) $$list2 '$(DESTDIR)$(libdir)'"; \ $(INSTALL_DATA) $$list2 "$(DESTDIR)$(libdir)" || exit $$?; } @$(POST_INSTALL) @list='$(lib_LIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ if test -f $$p; then \ $(am__strip_dir) \ echo " ( cd '$(DESTDIR)$(libdir)' && $(RANLIB) $$f )"; \ ( cd "$(DESTDIR)$(libdir)" && $(RANLIB) $$f ) || exit $$?; \ else :; fi; \ done uninstall-libLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LIBRARIES)'; test -n "$(libdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ test -n "$$files" || exit 0; \ echo " ( cd '$(DESTDIR)$(libdir)' && rm -f "$$files" )"; \ cd "$(DESTDIR)$(libdir)" && rm -f $$files clean-libLIBRARIES: -test -z "$(lib_LIBRARIES)" || rm -f $(lib_LIBRARIES) install-pkglibSCRIPTS: $(pkglib_SCRIPTS) @$(NORMAL_INSTALL) test -z "$(pkglibdir)" || $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" @list='$(pkglib_SCRIPTS)'; test -n "$(pkglibdir)" || list=; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(pkglibdir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(pkglibdir)$$dir" || exit $$?; \ } \ ; done uninstall-pkglibSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(pkglib_SCRIPTS)'; test -n "$(pkglibdir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibdir)" && rm -f $$files install-tclpkgSCRIPTS: $(tclpkg_SCRIPTS) @$(NORMAL_INSTALL) test -z "$(tclpkgdir)" || $(MKDIR_P) "$(DESTDIR)$(tclpkgdir)" @list='$(tclpkg_SCRIPTS)'; test -n "$(tclpkgdir)" || list=; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(tclpkgdir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(tclpkgdir)$$dir" || exit $$?; \ } \ ; done uninstall-tclpkgSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(tclpkg_SCRIPTS)'; test -n "$(tclpkgdir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(tclpkgdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(tclpkgdir)" && rm -f $$files mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkglibDATA: $(pkglib_DATA) @$(NORMAL_INSTALL) test -z "$(pkglibdir)" || $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" @list='$(pkglib_DATA)'; test -n "$(pkglibdir)" || list=; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkglibdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkglibdir)" || exit $$?; \ done uninstall-pkglibDATA: @$(NORMAL_UNINSTALL) @list='$(pkglib_DATA)'; test -n "$(pkglibdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ test -n "$$files" || exit 0; \ echo " ( cd '$(DESTDIR)$(pkglibdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(pkglibdir)" && rm -f $$files install-tclpkgDATA: $(tclpkg_DATA) @$(NORMAL_INSTALL) test -z "$(tclpkgdir)" || $(MKDIR_P) "$(DESTDIR)$(tclpkgdir)" @list='$(tclpkg_DATA)'; test -n "$(tclpkgdir)" || list=; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(tclpkgdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(tclpkgdir)" || exit $$?; \ done uninstall-tclpkgDATA: @$(NORMAL_UNINSTALL) @list='$(tclpkg_DATA)'; test -n "$(tclpkgdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ test -n "$$files" || exit 0; \ echo " ( cd '$(DESTDIR)$(tclpkgdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(tclpkgdir)" && rm -f $$files tags: TAGS TAGS: ctags: CTAGS CTAGS: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LIBRARIES) $(SCRIPTS) $(DATA) all-local installdirs: for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkglibdir)" "$(DESTDIR)$(tclpkgdir)" "$(DESTDIR)$(pkglibdir)" "$(DESTDIR)$(tclpkgdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libLIBRARIES clean-libtool \ mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-tclpkgDATA install-tclpkgSCRIPTS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-exec-local install-libLIBRARIES \ install-pkglibDATA install-pkglibSCRIPTS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-libLIBRARIES uninstall-pkglibDATA \ uninstall-pkglibSCRIPTS uninstall-tclpkgDATA \ uninstall-tclpkgSCRIPTS .MAKE: install-am install-strip .PHONY: all all-am all-local check check-am clean clean-generic \ clean-libLIBRARIES clean-libtool distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-exec-local \ install-html install-html-am install-info install-info-am \ install-libLIBRARIES install-man install-pdf install-pdf-am \ install-pkglibDATA install-pkglibSCRIPTS install-ps \ install-ps-am install-strip install-tclpkgDATA \ install-tclpkgSCRIPTS installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ uninstall uninstall-am uninstall-libLIBRARIES \ uninstall-pkglibDATA uninstall-pkglibSCRIPTS \ uninstall-tclpkgDATA uninstall-tclpkgSCRIPTS all-local: $(TCL_RRD_LIB) $(TCL_RRD_LIB): tclrrd.o $(TCL_SHLIB_LD) $(TCL_LD_SEARCH_FLAGS) $(LIBDIRS) $< -o $@ -lrrd_th -lm $(TCL_STUB_LIB_SPEC) $(LDFLAGS) $(LIBS) tclrrd.o: tclrrd.c $(CC) $(AM_CFLAGS) $(CFLAGS) $(TCL_SHLIB_CFLAGS) $(AM_CPPFLAGS) -c tclrrd.c -DVERSION=\"$(VERSION)\" pkgIndex.tcl: echo "package ifneeded Rrd $(VERSION) \"load $(libdir)/tclrrd$(VERSION)[info sharedlibextension]\"" > $@ install-exec-local: $(TCL_RRD_LIB) @$(NORMAL_INSTALL) $(INSTALL_PROGRAM) $(TCL_RRD_LIB) $(DESTDIR)$(libdir)/$(TCL_RRD_LIB) # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: rrdtool-1.4.7/bindings/tcl/Makefile.am0000644000175300017510000000322411070753447014561 00000000000000 EXTRA_DIST = README tclrrd.c VERSION = @VERSION@ AM_CFLAGS = @CFLAGS@ ### no including this by default @WERROR@ TCL_PREFIX = @TCL_PREFIX@ TCL_SHLIB_LD = @TCL_SHLIB_LD@ TCL_SHLIB_CFLAGS = @TCL_SHLIB_CFLAGS@ TCL_SHLIB_SUFFIX = @TCL_SHLIB_SUFFIX@ TCL_PACKAGE_PATH = @TCL_PACKAGE_PATH@ TCL_LD_SEARCH_FLAGS = @TCL_LD_SEARCH_FLAGS@ TCL_STUB_LIB_SPEC = @TCL_STUB_LIB_SPEC@ TCL_INCLUDE_SPEC = @TCL_INCLUDE_SPEC@ CLEANFILES = tclrrd.o tclrrd.so SRC_DIR = $(top_srcdir)/src AM_CPPFLAGS = $(TCL_INCLUDE_SPEC) -I$(SRC_DIR) -DUSE_TCL_STUBS LIBDIRS = -L$(top_builddir)/src/.libs -L$(top_builddir)/src -L$(libdir) LIB_RUNTIME_DIR = $(libdir) if BUILD_TCL_SITE tclpkgdir = @TCL_PACKAGE_DIR@ tclpkg_DATA = pkgIndex.tcl tclpkg_SCRIPTS = ifOctets.tcl else pkglib_DATA = pkgIndex.tcl pkglib_SCRIPTS = ifOctets.tcl endif # Automake doen't like `tclrrd$(VERSION)$(TCL_SHLIB_SUFFIX)' as # library name. So we build and install this library `by hand'. # # We do, however, specify a lib_LIBRARIES target such that # automake creates the directory (if neecessary). # TCL_RRD_LIB = tclrrd$(VERSION)$(TCL_SHLIB_SUFFIX) lib_LIBRARIES = all-local: $(TCL_RRD_LIB) $(TCL_RRD_LIB): tclrrd.o $(TCL_SHLIB_LD) $(TCL_LD_SEARCH_FLAGS) $(LIBDIRS) $< -o $@ -lrrd_th -lm $(TCL_STUB_LIB_SPEC) $(LDFLAGS) $(LIBS) tclrrd.o: tclrrd.c $(CC) $(AM_CFLAGS) $(CFLAGS) $(TCL_SHLIB_CFLAGS) $(AM_CPPFLAGS) -c tclrrd.c -DVERSION=\"$(VERSION)\" pkgIndex.tcl: echo "package ifneeded Rrd $(VERSION) \"load $(libdir)/tclrrd$(VERSION)[info sharedlibextension]\"" > $@ install-exec-local: $(TCL_RRD_LIB) @$(NORMAL_INSTALL) $(INSTALL_PROGRAM) $(TCL_RRD_LIB) $(DESTDIR)$(libdir)/$(TCL_RRD_LIB) rrdtool-1.4.7/bindings/tcl/README0000644000175300017510000000272410766723564013421 00000000000000TCLRRD -- A TCL interpreter extension to access the RRD library, contributed to Tobias Oetiker's RRD tools. Copyright (c) 1999,2000 Frank Strauss, Technical University of Braunschweig. See the file "COPYING" for information on usage and redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. TCLRRD adds a dynamically loadable package to the Tcl 8.x interpreter to access all RRD functions as of RRDtool 1.0.13. All command names and arguments are equal to those of RRDtool. They are assigned to the namespace `Rrd', e.g. `Rrd::create'. Return values are a bit different from plain RRDtool behavior to enable more native Tcl usage. Errors are mapped to the TCL_ERROR return code together with the RRD error strings. TCLRRD makes it easy to combine RRD use with advanced SNMP functionality of scotty (http://wwwsnmp.cs.utwente.nl/~schoenw/scotty/). E.g., it's easy to use some scotty code to get the counters of some interfaces by their interface name and then use Rrd::update to store the values. Furthermore, data source types (see RRD::create documentation) and integer value ranges could be easily retrieved from MIB information. TCLRRD has been written on a Linux system for use with Tcl 8.x. It should work on many other platforms, although it has not been tested. There are no fool proof installation procedures. Take a look at Makefile.am and adapt it, if required. TCLRRD has been written for RRD 1.0.13. Frank Strauss , 09-Mar-2000 rrdtool-1.4.7/bindings/Makefile.in0000644000175300017510000005350411707501506014010 00000000000000# Makefile.in generated by automake 1.11 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = bindings DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/acinclude.m4 \ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \ $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/rrd_config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ html-recursive info-recursive install-data-recursive \ install-dvi-recursive install-exec-recursive \ install-html-recursive install-info-recursive \ install-pdf-recursive install-ps-recursive install-recursive \ installcheck-recursive installdirs-recursive pdf-recursive \ ps-recursive uninstall-recursive RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ distdir ETAGS = etags CTAGS = ctags DIST_SUBDIRS = tcl lua DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ ALL_LIBS = @ALL_LIBS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ COMP_LUA = @COMP_LUA@ COMP_PERL = @COMP_PERL@ COMP_PYTHON = @COMP_PYTHON@ COMP_RUBY = @COMP_RUBY@ CORE_LIBS = @CORE_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ GMSGFMT = @GMSGFMT@ GMSGFMT_015 = @GMSGFMT_015@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTLLIBS = @INTLLIBS@ INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBVERS = @LIBVERS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ LUA = @LUA@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_DEFINES = @LUA_DEFINES@ LUA_INSTALL_CMOD = @LUA_INSTALL_CMOD@ LUA_INSTALL_LMOD = @LUA_INSTALL_LMOD@ LUA_LFLAGS = @LUA_LFLAGS@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ MSGFMT = @MSGFMT@ MSGFMT_015 = @MSGFMT_015@ MSGMERGE = @MSGMERGE@ MULTITHREAD_CFLAGS = @MULTITHREAD_CFLAGS@ MULTITHREAD_LDFLAGS = @MULTITHREAD_LDFLAGS@ NM = @NM@ NMEDIT = @NMEDIT@ NROFF = @NROFF@ NUMVERS = @NUMVERS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PERL = @PERL@ PERLCC = @PERLCC@ PERLCCFLAGS = @PERLCCFLAGS@ PERLFLAGS = @PERLFLAGS@ PERLLD = @PERLLD@ PERLLDFLAGS = @PERLLDFLAGS@ PERL_CC = @PERL_CC@ PERL_MAKE_OPTIONS = @PERL_MAKE_OPTIONS@ PERL_VERSION = @PERL_VERSION@ PKGCONFIG = @PKGCONFIG@ POD2HTML = @POD2HTML@ POD2MAN = @POD2MAN@ POSUB = @POSUB@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_INCLUDES = @PYTHON_INCLUDES@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ RRDDOCDIR = @RRDDOCDIR@ RRDGRAPH_YLEGEND_ANGLE = @RRDGRAPH_YLEGEND_ANGLE@ RRD_DEFAULT_FONT = @RRD_DEFAULT_FONT@ RRD_GETOPT_LONG = @RRD_GETOPT_LONG@ RUBY = @RUBY@ RUBY_MAKE_OPTIONS = @RUBY_MAKE_OPTIONS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ TCL_INCLUDE_SPEC = @TCL_INCLUDE_SPEC@ TCL_LD_SEARCH_FLAGS = @TCL_LD_SEARCH_FLAGS@ TCL_PACKAGE_DIR = @TCL_PACKAGE_DIR@ TCL_PACKAGE_PATH = @TCL_PACKAGE_PATH@ TCL_PREFIX = @TCL_PREFIX@ TCL_SHLIB_CFLAGS = @TCL_SHLIB_CFLAGS@ TCL_SHLIB_LD = @TCL_SHLIB_LD@ TCL_SHLIB_SUFFIX = @TCL_SHLIB_SUFFIX@ TCL_STUB_LIB_SPEC = @TCL_STUB_LIB_SPEC@ TCL_VERSION = @TCL_VERSION@ TROFF = @TROFF@ USE_NLS = @USE_NLS@ VERSION = @VERSION@ WERROR = @WERROR@ XGETTEXT = @XGETTEXT@ XGETTEXT_015 = @XGETTEXT_015@ XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ acx_pthread_config = @acx_pthread_config@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ @BUILD_TCL_TRUE@SUB_tcl = tcl @BUILD_LUA_TRUE@SUB_lua = lua SUBDIRS = $(SUB_tcl) $(SUB_lua) # the following files are not mentioned in any other Makefile EXTRA_DIST = perl-piped/MANIFEST perl-piped/README perl-piped/Makefile.PL perl-piped/RRDp.pm perl-piped/t/base.t \ perl-shared/ntmake-build perl-shared/MANIFEST perl-shared/README perl-shared/Makefile.PL perl-shared/RRDs.pm perl-shared/RRDs.xs perl-shared/t/base.t \ ruby/CHANGES ruby/README ruby/extconf.rb ruby/main.c ruby/test.rb \ python/ACKNOWLEDGEMENT python/AUTHORS python/COPYING python/README python/rrdtoolmodule.c python/setup.py all: all-recursive .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu bindings/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu bindings/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd # into them and run `make' without going through this Makefile. # To change the values of `make' variables: instead of editing Makefiles, # (1) if the variable is set in `config.status', edit `config.status' # (which will cause the Makefiles to be regenerated when you run `make'); # (2) otherwise, pass the desired values on the `make' command line. $(RECURSIVE_TARGETS): @failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ *k*) failcom='fail=yes';; \ esac; \ done; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ list='$(SUBDIRS)'; for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" $(RECURSIVE_CLEAN_TARGETS): @failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ *k*) failcom='fail=yes';; \ esac; \ done; \ dot_seen=no; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ rev=''; for subdir in $$list; do \ if test "$$subdir" = "."; then :; else \ rev="$$subdir $$rev"; \ fi; \ done; \ rev="$$rev ."; \ target=`echo $@ | sed s/-recursive//`; \ for subdir in $$rev; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done && test -z "$$fail" tags-recursive: list='$(SUBDIRS)'; for subdir in $$list; do \ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ done ctags-recursive: list='$(SUBDIRS)'; for subdir in $$list; do \ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ done ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: CTAGS CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile all-local installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool clean-local mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-data-local install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \ install-am install-strip tags-recursive .PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ all all-am all-local check check-am clean clean-generic \ clean-libtool clean-local ctags ctags-recursive distclean \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-data-local install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags tags-recursive uninstall uninstall-am .PHONY: python ruby # add the following to the all target all-local: @COMP_PERL@ @COMP_RUBY@ @COMP_PYTHON@ install-data-local: $(AM_V_GEN)test -f perl-piped/Makefile && cd perl-piped && $(MAKE) install || true $(AM_V_GEN)test -f perl-shared/Makefile && cd perl-shared && $(MAKE) install || true $(AM_V_GEN)test -f ruby/Makefile && cd ruby && $(MAKE) EPREFIX=$(DESTDIR)$(exec_prefix) $(RUBY_MAKE_OPTIONS) install || true $(AM_V_GEN)test -d python/build && cd python && env BUILDLIBDIR=../../src/.libs $(PYTHON) setup.py install --skip-build --prefix=$(DESTDIR)$(prefix) --exec-prefix=$(DESTDIR)$(exec_prefix) || true # rules for building the ruby module # RUBYARCHDIR= is to work around in a makefile quirk not sure # it is is the right thing todo, but it makes rrdtool build on freebsd as well ruby: Makefile cd ruby && $(RUBY) extconf.rb && $(MAKE) EPREFIX=$(exec_prefix) $(RUBY_MAKE_OPTIONS) RUBYARCHDIR= # rules for building the pyton module python: Makefile cd python && env BUILDLIBDIR=../../src/.libs $(PYTHON) setup.py build_ext --rpath=$(libdir) && env LIBDIR=../../src/.libs $(PYTHON) setup.py build # rules for building the perl module perl_piped: perl-piped/Makefile cd perl-piped && $(MAKE) perl-piped/Makefile: perl-piped/Makefile.PL cd perl-piped && $(PERL) Makefile.PL $(PERL_MAKE_OPTIONS) perl_shared: perl-shared/Makefile cd perl-shared && $(MAKE) perl-shared/Makefile: perl-shared/Makefile.PL Makefile cd perl-shared && $(PERL) Makefile.PL $(PERLFLAGS) $(PERL_MAKE_OPTIONS) RPATH=$(libdir) # LIBS="$(LDFLAGS) $(LIBS)" $(PERLFLAGS) $(PERL_MAKE_OPTIONS) clean-local: test -f perl-piped/Makefile && cd perl-piped && $(MAKE) clean || true test -f perl-piped/Makefile && rm perl-piped/Makefile || true test -f perl-shared/Makefile && cd perl-shared && $(MAKE) clean || true test -f perl-shared/Makefile && rm -f perl-shared/Makefile || true test -f ruby/Makefile && cd ruby && $(MAKE) clean && rm Makefile || true test -d python/build && cd python && rm -rf build || true # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: rrdtool-1.4.7/bindings/Makefile.am0000644000175300017510000000515711343241254013775 00000000000000.PHONY: python ruby if BUILD_TCL SUB_tcl = tcl endif if BUILD_LUA SUB_lua = lua endif SUBDIRS = $(SUB_tcl) $(SUB_lua) # the following files are not mentioned in any other Makefile EXTRA_DIST = perl-piped/MANIFEST perl-piped/README perl-piped/Makefile.PL perl-piped/RRDp.pm perl-piped/t/base.t \ perl-shared/ntmake-build perl-shared/MANIFEST perl-shared/README perl-shared/Makefile.PL perl-shared/RRDs.pm perl-shared/RRDs.xs perl-shared/t/base.t \ ruby/CHANGES ruby/README ruby/extconf.rb ruby/main.c ruby/test.rb \ python/ACKNOWLEDGEMENT python/AUTHORS python/COPYING python/README python/rrdtoolmodule.c python/setup.py # add the following to the all target all-local: @COMP_PERL@ @COMP_RUBY@ @COMP_PYTHON@ install-data-local: $(AM_V_GEN)test -f perl-piped/Makefile && cd perl-piped && $(MAKE) install || true $(AM_V_GEN)test -f perl-shared/Makefile && cd perl-shared && $(MAKE) install || true $(AM_V_GEN)test -f ruby/Makefile && cd ruby && $(MAKE) EPREFIX=$(DESTDIR)$(exec_prefix) $(RUBY_MAKE_OPTIONS) install || true $(AM_V_GEN)test -d python/build && cd python && env BUILDLIBDIR=../../src/.libs $(PYTHON) setup.py install --skip-build --prefix=$(DESTDIR)$(prefix) --exec-prefix=$(DESTDIR)$(exec_prefix) || true # rules for building the ruby module # RUBYARCHDIR= is to work around in a makefile quirk not sure # it is is the right thing todo, but it makes rrdtool build on freebsd as well ruby: Makefile cd ruby && $(RUBY) extconf.rb && $(MAKE) EPREFIX=$(exec_prefix) $(RUBY_MAKE_OPTIONS) RUBYARCHDIR= # rules for building the pyton module python: Makefile cd python && env BUILDLIBDIR=../../src/.libs $(PYTHON) setup.py build_ext --rpath=$(libdir) && env LIBDIR=../../src/.libs $(PYTHON) setup.py build # rules for building the perl module perl_piped: perl-piped/Makefile cd perl-piped && $(MAKE) perl-piped/Makefile: perl-piped/Makefile.PL cd perl-piped && $(PERL) Makefile.PL $(PERL_MAKE_OPTIONS) perl_shared: perl-shared/Makefile cd perl-shared && $(MAKE) perl-shared/Makefile: perl-shared/Makefile.PL Makefile cd perl-shared && $(PERL) Makefile.PL $(PERLFLAGS) $(PERL_MAKE_OPTIONS) RPATH=$(libdir) # LIBS="$(LDFLAGS) $(LIBS)" $(PERLFLAGS) $(PERL_MAKE_OPTIONS) clean-local: test -f perl-piped/Makefile && cd perl-piped && $(MAKE) clean || true test -f perl-piped/Makefile && rm perl-piped/Makefile || true test -f perl-shared/Makefile && cd perl-shared && $(MAKE) clean || true test -f perl-shared/Makefile && rm -f perl-shared/Makefile || true test -f ruby/Makefile && cd ruby && $(MAKE) clean && rm Makefile || true test -d python/build && cd python && rm -rf build || true ##END## rrdtool-1.4.7/bindings/ruby/0000777000175300017510000000000011707501552013002 500000000000000rrdtool-1.4.7/bindings/ruby/test.rb0000755000175300017510000000363611333061531014226 00000000000000#!/usr/bin/env ruby # $Id: test.rb,v 1.2 2002/10/22 17:34:00 miles Exp $ # Driver does not carry cash. $: << '/scratch/rrd12build/lib/ruby/1.8/i386-linux/' require "RRD" name = "test" rrd = "#{name}.rrd" start_time = Time.now.to_i end_time = start_time.to_i + 300 * 300 puts "creating #{rrd}" RRD.create( rrd, "--start", "#{start_time - 1}", "--step", "300", "DS:a:GAUGE:600:U:U", "DS:b:GAUGE:600:U:U", "RRA:AVERAGE:0.5:1:300") puts puts "updating #{rrd}" start_time.step(end_time, 300) { |i| RRD.update(rrd, "#{i}:#{rand(100)}:#{Math.sin(i / 800) * 50 + 50}") } puts puts "fetching data from #{rrd}" (fstart, fend, data) = RRD.fetch(rrd, "--start", start_time.to_s, "--end", end_time.to_s, "AVERAGE") puts "got #{data.length} data points from #{fstart} to #{fend}" puts puts "generating graph #{name}.png" RRD.graph( "#{name}.png", "--title", " RubyRRD Demo", "--start", "#{start_time+3600}", "--end", "start + 1000 min", "--interlace", "--imgformat", "PNG", "--width=450", "DEF:a=#{rrd}:a:AVERAGE", "DEF:b=#{rrd}:b:AVERAGE", "CDEF:line=TIME,2400,%,300,LT,a,UNKN,IF", "AREA:b#00b6e4:beta", "AREA:line#0022e9:alpha", "LINE3:line#ff0000") puts # last method test if end_time != RRD.last("#{rrd}").to_i puts "last method expects #{Time.at(end_time)}." puts " But #{RRD.last("#{rrd}")} returns." end puts # xport method test puts "xporting data from #{rrd}" (fstart,fend,step,col,legend,data)=RRD.xport( "--start", start_time.to_s, "--end", (start_time + 300 * 300).to_s, "--step", 10.to_s, "DEF:A=#{rrd}:a:AVERAGE", "DEF:B=#{rrd}:b:AVERAGE", "XPORT:A:a", "XPORT:B:b") puts "Xported #{col} columns(#{legend.join(", ")}) with #{data.length} rows from #{fstart} to #{fend} and step #{step}\n" print "This script has created #{name}.png in the current directory\n"; print "This demonstrates the use of the TIME and % RPN operators\n"; rrdtool-1.4.7/bindings/ruby/main.c0000644000175300017510000002123011673352221014003 00000000000000/* $Id: main.c 2246 2011-12-18 11:54:57Z oetiker $ * Substantial penalty for early withdrawal. */ #include #include #include #include "../../src/rrd_tool.h" typedef struct string_arr_t { int len; char **strings; } string_arr; VALUE mRRD; VALUE rb_eRRDError; typedef int ( *RRDFUNC) ( int argc, char **argv); typedef rrd_info_t *( *RRDINFOFUNC) ( int argc, char **argv); #define RRD_CHECK_ERROR \ if (rrd_test_error()) \ rb_raise(rb_eRRDError, rrd_get_error()); \ rrd_clear_error(); string_arr string_arr_new( VALUE rb_strings) { string_arr a; char buf[64]; int i; Check_Type(rb_strings, T_ARRAY); a.len = RARRAY_LEN(rb_strings) + 1; a.strings = malloc(a.len * sizeof(char *)); a.strings[0] = "dummy"; /* first element is a dummy element */ for (i = 0; i < a.len - 1; i++) { VALUE v = rb_ary_entry(rb_strings, i); switch (TYPE(v)) { case T_STRING: a.strings[i + 1] = strdup(StringValuePtr(v)); break; case T_FIXNUM: snprintf(buf, 63, "%ld", (long)FIX2INT(v)); a.strings[i + 1] = strdup(buf); break; default: rb_raise(rb_eTypeError, "invalid argument - %s, expected T_STRING or T_FIXNUM on index %ld", (long)rb_class2name(CLASS_OF(v)), i); break; } } return a; } void string_arr_delete( string_arr a) { int i; /* skip dummy first entry */ for (i = 1; i < a.len; i++) { free(a.strings[i]); } free(a.strings); } void reset_rrd_state( ) { optind = 0; opterr = 0; rrd_clear_error(); } /* Simple Calls */ VALUE rrd_call( RRDFUNC func, VALUE args) { string_arr a; a = string_arr_new(args); reset_rrd_state(); func(a.len, a.strings); string_arr_delete(a); RRD_CHECK_ERROR return Qnil; } VALUE rb_rrd_create( VALUE self, VALUE args) { return rrd_call(rrd_create, args); } VALUE rb_rrd_dump( VALUE self, VALUE args) { return rrd_call(rrd_dump, args); } VALUE rb_rrd_resize( VALUE self, VALUE args) { return rrd_call(rrd_resize, args); } VALUE rb_rrd_restore( VALUE self, VALUE args) { return rrd_call(rrd_restore, args); } VALUE rb_rrd_tune( VALUE self, VALUE args) { return rrd_call(rrd_tune, args); } VALUE rb_rrd_update( VALUE self, VALUE args) { return rrd_call(rrd_update, args); } VALUE rb_rrd_flushcached( VALUE self, VALUE args) { return rrd_call(rrd_flushcached, args); } /* Calls Returning Data via the Info Interface */ VALUE rb_rrd_infocall( RRDINFOFUNC func, VALUE args) { string_arr a; rrd_info_t *p, *data; VALUE result; a = string_arr_new(args); reset_rrd_state(); data = func(a.len, a.strings); string_arr_delete(a); RRD_CHECK_ERROR result = rb_hash_new(); p = data; while (data) { VALUE key = rb_str_new2(data->key); switch (data->type) { case RD_I_VAL: if (isnan(data->value.u_val)) { rb_hash_aset(result, key, Qnil); } else { rb_hash_aset(result, key, rb_float_new(data->value.u_val)); } break; case RD_I_CNT: rb_hash_aset(result, key, INT2FIX(data->value.u_cnt)); break; case RD_I_STR: rb_hash_aset(result, key, rb_str_new2(data->value.u_str)); break; case RD_I_INT: rb_hash_aset(result, key, INT2FIX(data->value.u_int)); break; case RD_I_BLO: rb_hash_aset(result, key, rb_str_new((char *)data->value.u_blo.ptr, data->value.u_blo.size)); break; } data = data->next; } rrd_info_free(p); return result; } VALUE rb_rrd_info( VALUE self, VALUE args) { return rb_rrd_infocall(rrd_info, args); } VALUE rb_rrd_updatev( VALUE self, VALUE args) { return rb_rrd_infocall(rrd_update_v, args); } VALUE rb_rrd_graphv( VALUE self, VALUE args) { return rb_rrd_infocall(rrd_graph_v, args); } /* Other Calls */ VALUE rb_rrd_fetch( VALUE self, VALUE args) { string_arr a; unsigned long i, j, k, step, ds_cnt; rrd_value_t *raw_data; char **raw_names; VALUE data, names, result; time_t start, end; a = string_arr_new(args); reset_rrd_state(); rrd_fetch(a.len, a.strings, &start, &end, &step, &ds_cnt, &raw_names, &raw_data); string_arr_delete(a); RRD_CHECK_ERROR names = rb_ary_new(); for (i = 0; i < ds_cnt; i++) { rb_ary_push(names, rb_str_new2(raw_names[i])); rrd_freemem(raw_names[i]); } rrd_freemem(raw_names); k = 0; data = rb_ary_new(); for (i = start; i <= end; i += step) { VALUE line = rb_ary_new2(ds_cnt); for (j = 0; j < ds_cnt; j++) { rb_ary_store(line, j, rb_float_new(raw_data[k])); k++; } rb_ary_push(data, line); } rrd_freemem(raw_data); result = rb_ary_new2(5); rb_ary_store(result, 0, INT2NUM(start)); rb_ary_store(result, 1, INT2NUM(end)); rb_ary_store(result, 2, names); rb_ary_store(result, 3, data); rb_ary_store(result, 4, INT2FIX(step)); return result; } VALUE rb_rrd_graph( VALUE self, VALUE args) { string_arr a; char **calcpr, **p; VALUE result, print_results; int xsize, ysize; double ymin, ymax; a = string_arr_new(args); reset_rrd_state(); rrd_graph(a.len, a.strings, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax); string_arr_delete(a); RRD_CHECK_ERROR result = rb_ary_new2(3); print_results = rb_ary_new(); p = calcpr; for (p = calcpr; p && *p; p++) { rb_ary_push(print_results, rb_str_new2(*p)); rrd_freemem(*p); } rrd_freemem(calcpr); rb_ary_store(result, 0, print_results); rb_ary_store(result, 1, INT2FIX(xsize)); rb_ary_store(result, 2, INT2FIX(ysize)); return result; } VALUE rb_rrd_last( VALUE self, VALUE args) { string_arr a; time_t last; a = string_arr_new(args); reset_rrd_state(); last = rrd_last(a.len, a.strings); string_arr_delete(a); RRD_CHECK_ERROR return rb_funcall(rb_cTime, rb_intern("at"), 1, UINT2NUM(last)); } VALUE rb_rrd_xport( VALUE self, VALUE args) { string_arr a; unsigned long i, j, k, step, col_cnt; int xxsize; rrd_value_t *data; char **legend_v; VALUE legend, result, rdata; time_t start, end; a = string_arr_new(args); reset_rrd_state(); rrd_xport(a.len, a.strings, &xxsize, &start, &end, &step, &col_cnt, &legend_v, &data); string_arr_delete(a); RRD_CHECK_ERROR; legend = rb_ary_new(); for (i = 0; i < col_cnt; i++) { rb_ary_push(legend, rb_str_new2(legend_v[i])); free(legend_v[i]); } free(legend_v); k = 0; rdata = rb_ary_new(); for (i = start; i <= end; i += step) { VALUE line = rb_ary_new2(col_cnt); for (j = 0; j < col_cnt; j++) { rb_ary_store(line, j, rb_float_new(data[k])); k++; } rb_ary_push(rdata, line); } free(data); result = rb_ary_new2(6); rb_ary_store(result, 0, INT2FIX(start)); rb_ary_store(result, 1, INT2FIX(end)); rb_ary_store(result, 2, INT2FIX(step)); rb_ary_store(result, 3, INT2FIX(col_cnt)); rb_ary_store(result, 4, legend); rb_ary_store(result, 5, rdata); return result; } void Init_RRD( ) { mRRD = rb_define_module("RRD"); rb_eRRDError = rb_define_class("RRDError", rb_eStandardError); rb_define_module_function(mRRD, "create", rb_rrd_create, -2); rb_define_module_function(mRRD, "dump", rb_rrd_dump, -2); rb_define_module_function(mRRD, "fetch", rb_rrd_fetch, -2); rb_define_module_function(mRRD, "graph", rb_rrd_graph, -2); rb_define_module_function(mRRD, "last", rb_rrd_last, -2); rb_define_module_function(mRRD, "resize", rb_rrd_resize, -2); rb_define_module_function(mRRD, "restore", rb_rrd_restore, -2); rb_define_module_function(mRRD, "tune", rb_rrd_tune, -2); rb_define_module_function(mRRD, "update", rb_rrd_update, -2); rb_define_module_function(mRRD, "flushcached", rb_rrd_flushcached, -2); rb_define_module_function(mRRD, "info", rb_rrd_info, -2); rb_define_module_function(mRRD, "updatev", rb_rrd_updatev, -2); rb_define_module_function(mRRD, "graphv", rb_rrd_graphv, -2); rb_define_module_function(mRRD, "xport", rb_rrd_xport, -2); } rrdtool-1.4.7/bindings/ruby/extconf.rb0000644000175300017510000000076211503577714014725 00000000000000# $Id: extconf.rb,v 1.2 2001/11/28 18:30:16 miles Exp $ # Lost ticket pays maximum rate. require 'mkmf' if /linux/ =~ RUBY_PLATFORM $LDFLAGS += ' -Wl,--rpath -Wl,$(EPREFIX)/lib' elsif /solaris/ =~ RUBY_PLATFORM $LDFLAGS += ' -R$(EPREFIX)/lib' elsif /hpux/ =~ RUBY_PLATFORM $LDFLAGS += ' +b$(EPREFIX)/lib' elsif /aix/ =~ RUBY_PLATFORM $LDFLAGS += ' -blibpath:$(EPREFIX)/lib' end dir_config("rrd","../../src","../../src/.libs") have_library("rrd", "rrd_create") create_makefile("RRD") rrdtool-1.4.7/bindings/ruby/README0000644000175300017510000000103610766723564013613 00000000000000# # ruby librrd bindings # author: Miles Egan # - Introduction This module provides ruby bindings for librrd, with functionality comparable to the native perl bindings. See test.rb for a script that exercises all ruby-librrd functionality. - Installation Installation is standard. Simply run: ruby extconf.rb make make install I hope this works for you. Please let me know if you have any problems or suggestions. Someday when I'm feeling less lazy I'll actually document this thing. Thanks to Tobi for rrdtool! rrdtool-1.4.7/bindings/ruby/CHANGES0000644000175300017510000000033211333061531013700 000000000000002010-02-05 Pavel Pachkovskij (Azati corp.) add rrd_xport update test.rb with rrd_xport example 2006-07-25 Loïs Lherbier add y_min and y_max parameters to rrd_graph update test.rb to send only strings to RRD.fetch rrdtool-1.4.7/bindings/python/0000777000175300017510000000000011707501552013342 500000000000000rrdtool-1.4.7/bindings/python/setup.py0000644000175300017510000000340010766723564015002 00000000000000#! /usr/bin/env python # # setup.py # # py-rrdtool distutil setup # # Author : Hye-Shik Chang # Date : $Date: 2003/02/14 02:38:16 $ # Created : 24 May 2002 # # $Revision: 1.7 $ # # ========================================================================== # This file is part of py-rrdtool. # # py-rrdtool is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # py-rrdtool is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with Foobar; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # from distutils.core import setup, Extension import sys, os RRDBASE = os.environ.get('LOCALBASE', '../../src') library_dir = os.environ.get('BUILDLIBDIR', os.path.join(RRDBASE, '.libs')) include_dir = os.environ.get('INCDIR', RRDBASE) setup(name = "py-rrdtool", version = "0.2.1", description = "Python Interface to RRDTool", author = "Hye-Shik Chang", author_email = "perky@fallin.lv", license = "LGPL", url = "http://oss.oetiker.ch/rrdtool", #packages = ['rrdtool'], ext_modules = [ Extension( "rrdtoolmodule", ["rrdtoolmodule.c"], libraries=['rrd'], library_dirs=[library_dir], include_dirs=[include_dir], ) ] ) rrdtool-1.4.7/bindings/python/rrdtoolmodule.c0000644000175300017510000004201211236742013016310 00000000000000/* * rrdtoolmodule.c * * RRDTool Python binding * * Author : Hye-Shik Chang * Date : $Date: 2003/02/22 07:41:19 $ * Created : 23 May 2002 * * $Revision: 1.14 $ * * ========================================================================== * This file is part of py-rrdtool. * * py-rrdtool is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * py-rrdtool is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Foobar; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifdef UNUSED #elif defined(__GNUC__) # define UNUSED(x) x __attribute__((unused)) #elif defined(__LCLINT__) # define UNUSED(x) /*@unused@*/ x #else # define UNUSED(x) x #endif #include "../../rrd_config.h" static const char *__version__ = PACKAGE_VERSION; #include "Python.h" #include "../../src/rrd_tool.h" //#include "rrd.h" //#include "rrd_extra.h" static PyObject *ErrorObject; extern int optind; extern int opterr; /* forward declaration to keep compiler happy */ void initrrdtool( void); static int create_args( char *command, PyObject * args, int *argc, char ***argv) { PyObject *o, *lo; int args_count, argv_count, element_count, i, j; args_count = PyTuple_Size(args); element_count = 0; for (i = 0; i < args_count; i++) { o = PyTuple_GET_ITEM(args, i); if (PyString_Check(o)) element_count++; else if (PyList_CheckExact(o)) element_count += PyList_Size(o); else { PyErr_Format(PyExc_TypeError, "argument %d must be string or list of strings", i); return -1; } } *argv = PyMem_New(char *, element_count + 1); if (*argv == NULL) return -1; argv_count = 0; for (i = 0; i < args_count; i++) { o = PyTuple_GET_ITEM(args, i); if (PyString_Check(o)) { argv_count++; (*argv)[argv_count] = PyString_AS_STRING(o); } else if (PyList_CheckExact(o)) for (j = 0; j < PyList_Size(o); j++) { lo = PyList_GetItem(o, j); if (PyString_Check(lo)) { argv_count++; (*argv)[argv_count] = PyString_AS_STRING(lo); } else { PyMem_Del(*argv); PyErr_Format(PyExc_TypeError, "element %d in argument %d must be string", j, i); return -1; } } else { PyMem_Del(*argv); PyErr_Format(PyExc_TypeError, "argument %d must be string or list of strings", i); return -1; } } (*argv)[0] = command; *argc = element_count + 1; /* reset getopt state */ opterr = optind = 0; return 0; } static void destroy_args( char ***argv) { PyMem_Del(*argv); *argv = NULL; } static char PyRRD_create__doc__[] = "create(args..): Set up a new Round Robin Database\n\ create filename [--start|-b start time] \ [--step|-s step] [DS:ds-name:DST:heartbeat:min:max] \ [RRA:CF:xff:steps:rows]"; static PyObject *PyRRD_create( PyObject UNUSED(*self), PyObject * args) { PyObject *r; char **argv; int argc; if (create_args("create", args, &argc, &argv) < 0) return NULL; if (rrd_create(argc, argv) == -1) { PyErr_SetString(ErrorObject, rrd_get_error()); rrd_clear_error(); r = NULL; } else { Py_INCREF(Py_None); r = Py_None; } destroy_args(&argv); return r; } static char PyRRD_update__doc__[] = "update(args..): Store a new set of values into the rrd\n" " update filename [--template|-t ds-name[:ds-name]...] " "N|timestamp:value[:value...] [timestamp:value[:value...] ...]"; static PyObject *PyRRD_update( PyObject UNUSED(*self), PyObject * args) { PyObject *r; char **argv; int argc; if (create_args("update", args, &argc, &argv) < 0) return NULL; if (rrd_update(argc, argv) == -1) { PyErr_SetString(ErrorObject, rrd_get_error()); rrd_clear_error(); r = NULL; } else { Py_INCREF(Py_None); r = Py_None; } destroy_args(&argv); return r; } static char PyRRD_fetch__doc__[] = "fetch(args..): fetch data from an rrd.\n" " fetch filename CF [--resolution|-r resolution] " "[--start|-s start] [--end|-e end]"; static PyObject *PyRRD_fetch( PyObject UNUSED(*self), PyObject * args) { PyObject *r; rrd_value_t *data, *datai; unsigned long step, ds_cnt; time_t start, end; int argc; char **argv, **ds_namv; if (create_args("fetch", args, &argc, &argv) < 0) return NULL; if (rrd_fetch(argc, argv, &start, &end, &step, &ds_cnt, &ds_namv, &data) == -1) { PyErr_SetString(ErrorObject, rrd_get_error()); rrd_clear_error(); r = NULL; } else { /* Return : ((start, end, step), (name1, name2, ...), [(data1, data2, ..), ...]) */ PyObject *range_tup, *dsnam_tup, *data_list, *t; unsigned long i, j, row; rrd_value_t dv; row = (end - start) / step; r = PyTuple_New(3); range_tup = PyTuple_New(3); dsnam_tup = PyTuple_New(ds_cnt); data_list = PyList_New(row); PyTuple_SET_ITEM(r, 0, range_tup); PyTuple_SET_ITEM(r, 1, dsnam_tup); PyTuple_SET_ITEM(r, 2, data_list); datai = data; PyTuple_SET_ITEM(range_tup, 0, PyInt_FromLong((long) start)); PyTuple_SET_ITEM(range_tup, 1, PyInt_FromLong((long) end)); PyTuple_SET_ITEM(range_tup, 2, PyInt_FromLong((long) step)); for (i = 0; i < ds_cnt; i++) PyTuple_SET_ITEM(dsnam_tup, i, PyString_FromString(ds_namv[i])); for (i = 0; i < row; i++) { t = PyTuple_New(ds_cnt); PyList_SET_ITEM(data_list, i, t); for (j = 0; j < ds_cnt; j++) { dv = *(datai++); if (isnan(dv)) { PyTuple_SET_ITEM(t, j, Py_None); Py_INCREF(Py_None); } else { PyTuple_SET_ITEM(t, j, PyFloat_FromDouble((double) dv)); } } } for (i = 0; i < ds_cnt; i++) rrd_freemem(ds_namv[i]); rrd_freemem(ds_namv); /* rrdtool don't use PyMem_Malloc :) */ rrd_freemem(data); } destroy_args(&argv); return r; } static char PyRRD_graph__doc__[] = "graph(args..): Create a graph based on data from one or several RRD\n" " graph filename [-s|--start seconds] " "[-e|--end seconds] [-x|--x-grid x-axis grid and label] " "[-y|--y-grid y-axis grid and label] [--alt-y-grid] [--alt-y-mrtg] " "[--alt-autoscale] [--alt-autoscale-max] [--units-exponent] value " "[-v|--vertical-label text] [-w|--width pixels] [-h|--height pixels] " "[-i|--interlaced] " "[-f|--imginfo formatstring] [-a|--imgformat GIF|PNG|GD] " "[-B|--background value] [-O|--overlay value] " "[-U|--unit value] [-z|--lazy] [-o|--logarithmic] " "[-u|--upper-limit value] [-l|--lower-limit value] " "[-g|--no-legend] [-r|--rigid] [--step value] " "[-b|--base value] [-c|--color COLORTAG#rrggbb] " "[-t|--title title] [DEF:vname=rrd:ds-name:CF] " "[CDEF:vname=rpn-expression] [PRINT:vname:CF:format] " "[GPRINT:vname:CF:format] [COMMENT:text] " "[HRULE:value#rrggbb[:legend]] [VRULE:time#rrggbb[:legend]] " "[LINE{1|2|3}:vname[#rrggbb[:legend]]] " "[AREA:vname[#rrggbb[:legend]]] " "[STACK:vname[#rrggbb[:legend]]]"; static PyObject *PyRRD_graph( PyObject UNUSED(*self), PyObject * args) { PyObject *r; char **argv, **calcpr; int argc, xsize, ysize, i; double ymin, ymax; if (create_args("graph", args, &argc, &argv) < 0) return NULL; if (rrd_graph(argc, argv, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax) == -1) { PyErr_SetString(ErrorObject, rrd_get_error()); rrd_clear_error(); r = NULL; } else { r = PyTuple_New(3); PyTuple_SET_ITEM(r, 0, PyInt_FromLong((long) xsize)); PyTuple_SET_ITEM(r, 1, PyInt_FromLong((long) ysize)); if (calcpr) { PyObject *e, *t; e = PyList_New(0); PyTuple_SET_ITEM(r, 2, e); for (i = 0; calcpr[i]; i++) { t = PyString_FromString(calcpr[i]); PyList_Append(e, t); Py_DECREF(t); rrd_freemem(calcpr[i]); } rrd_freemem(calcpr); } else { Py_INCREF(Py_None); PyTuple_SET_ITEM(r, 2, Py_None); } } destroy_args(&argv); return r; } static char PyRRD_tune__doc__[] = "tune(args...): Modify some basic properties of a Round Robin Database\n" " tune filename [--heartbeat|-h ds-name:heartbeat] " "[--minimum|-i ds-name:min] [--maximum|-a ds-name:max] " "[--data-source-type|-d ds-name:DST] [--data-source-rename|-r old-name:new-name]"; static PyObject *PyRRD_tune( PyObject UNUSED(*self), PyObject * args) { PyObject *r; char **argv; int argc; if (create_args("tune", args, &argc, &argv) < 0) return NULL; if (rrd_tune(argc, argv) == -1) { PyErr_SetString(ErrorObject, rrd_get_error()); rrd_clear_error(); r = NULL; } else { Py_INCREF(Py_None); r = Py_None; } destroy_args(&argv); return r; } static char PyRRD_first__doc__[] = "first(filename): Return the timestamp of the first data sample in an RRD"; static PyObject *PyRRD_first( PyObject UNUSED(*self), PyObject * args) { PyObject *r; int argc, ts; char **argv; if (create_args("first", args, &argc, &argv) < 0) return NULL; if ((ts = rrd_first(argc, argv)) == -1) { PyErr_SetString(ErrorObject, rrd_get_error()); rrd_clear_error(); r = NULL; } else r = PyInt_FromLong((long) ts); destroy_args(&argv); return r; } static char PyRRD_last__doc__[] = "last(filename): Return the timestamp of the last data sample in an RRD"; static PyObject *PyRRD_last( PyObject UNUSED(*self), PyObject * args) { PyObject *r; int argc, ts; char **argv; if (create_args("last", args, &argc, &argv) < 0) return NULL; if ((ts = rrd_last(argc, argv)) == -1) { PyErr_SetString(ErrorObject, rrd_get_error()); rrd_clear_error(); r = NULL; } else r = PyInt_FromLong((long) ts); destroy_args(&argv); return r; } static char PyRRD_resize__doc__[] = "resize(args...): alters the size of an RRA.\n" " resize filename rra-num GROW|SHRINK rows"; static PyObject *PyRRD_resize( PyObject UNUSED(*self), PyObject * args) { PyObject *r; char **argv; int argc, ts; if (create_args("resize", args, &argc, &argv) < 0) return NULL; if ((ts = rrd_resize(argc, argv)) == -1) { PyErr_SetString(ErrorObject, rrd_get_error()); rrd_clear_error(); r = NULL; } else { Py_INCREF(Py_None); r = Py_None; } destroy_args(&argv); return r; } static PyObject *PyDict_FromInfo( rrd_info_t * data) { PyObject *r; r = PyDict_New(); while (data) { PyObject *val = NULL; switch (data->type) { case RD_I_VAL: val = isnan(data->value.u_val) ? (Py_INCREF(Py_None), Py_None) : PyFloat_FromDouble(data->value.u_val); break; case RD_I_CNT: val = PyLong_FromUnsignedLong(data->value.u_cnt); break; case RD_I_INT: val = PyLong_FromLong(data->value.u_int); break; case RD_I_STR: val = PyString_FromString(data->value.u_str); break; case RD_I_BLO: val = PyString_FromStringAndSize((char *) data->value.u_blo.ptr, data->value.u_blo.size); break; } if (val) { PyDict_SetItemString(r, data->key, val); Py_DECREF(val); } data = data->next; } return r; } static char PyRRD_info__doc__[] = "info(filename): extract header information from an rrd"; static PyObject *PyRRD_info( PyObject UNUSED(*self), PyObject * args) { PyObject *r; int argc; char **argv; rrd_info_t *data; if (create_args("info", args, &argc, &argv) < 0) return NULL; if ((data = rrd_info(argc, argv)) == NULL) { PyErr_SetString(ErrorObject, rrd_get_error()); rrd_clear_error(); r = NULL; } else { r = PyDict_FromInfo(data); rrd_info_free(data); } destroy_args(&argv); return r; } static char PyRRD_graphv__doc__[] = "graphv is called in the same manner as graph"; static PyObject *PyRRD_graphv( PyObject UNUSED(*self), PyObject * args) { PyObject *r; int argc; char **argv; rrd_info_t *data; if (create_args("graphv", args, &argc, &argv) < 0) return NULL; if ((data = rrd_graph_v(argc, argv)) == NULL) { PyErr_SetString(ErrorObject, rrd_get_error()); rrd_clear_error(); r = NULL; } else { r = PyDict_FromInfo(data); rrd_info_free(data); } destroy_args(&argv); return r; } static char PyRRD_updatev__doc__[] = "updatev is called in the same manner as update"; static PyObject *PyRRD_updatev( PyObject UNUSED(*self), PyObject * args) { PyObject *r; int argc; char **argv; rrd_info_t *data; if (create_args("updatev", args, &argc, &argv) < 0) return NULL; if ((data = rrd_update_v(argc, argv)) == NULL) { PyErr_SetString(ErrorObject, rrd_get_error()); rrd_clear_error(); r = NULL; } else { r = PyDict_FromInfo(data); rrd_info_free(data); } destroy_args(&argv); return r; } static char PyRRD_flushcached__doc__[] = "flush(args..): flush RRD files from memory\n" " flush [--daemon address] file [file ...]"; static PyObject *PyRRD_flushcached( PyObject UNUSED(*self), PyObject * args) { PyObject *r; int argc; char **argv; if (create_args("flushcached", args, &argc, &argv) < 0) return NULL; if (rrd_flushcached(argc, argv) != 0) { PyErr_SetString(ErrorObject, rrd_get_error()); rrd_clear_error(); r = NULL; } else { Py_INCREF(Py_None); r = Py_None; } destroy_args(&argv); return r; } /* List of methods defined in the module */ #define meth(name, func, doc) {name, (PyCFunction)func, METH_VARARGS, doc} static PyMethodDef _rrdtool_methods[] = { meth("create", PyRRD_create, PyRRD_create__doc__), meth("update", PyRRD_update, PyRRD_update__doc__), meth("fetch", PyRRD_fetch, PyRRD_fetch__doc__), meth("graph", PyRRD_graph, PyRRD_graph__doc__), meth("tune", PyRRD_tune, PyRRD_tune__doc__), meth("first", PyRRD_first, PyRRD_first__doc__), meth("last", PyRRD_last, PyRRD_last__doc__), meth("resize", PyRRD_resize, PyRRD_resize__doc__), meth("info", PyRRD_info, PyRRD_info__doc__), meth("graphv", PyRRD_graphv, PyRRD_graphv__doc__), meth("updatev", PyRRD_updatev, PyRRD_updatev__doc__), meth("flushcached", PyRRD_flushcached, PyRRD_flushcached__doc__), {NULL, NULL, 0, NULL} }; #define SET_INTCONSTANT(dict, value) \ t = PyInt_FromLong((long)value); \ PyDict_SetItemString(dict, #value, t); \ Py_DECREF(t); #define SET_STRCONSTANT(dict, value) \ t = PyString_FromString(value); \ PyDict_SetItemString(dict, #value, t); \ Py_DECREF(t); /* Initialization function for the module */ void initrrdtool( void) { PyObject *m, *d, *t; /* Create the module and add the functions */ m = Py_InitModule("rrdtool", _rrdtool_methods); /* Add some symbolic constants to the module */ d = PyModule_GetDict(m); SET_STRCONSTANT(d, __version__); ErrorObject = PyErr_NewException("rrdtool.error", NULL, NULL); PyDict_SetItemString(d, "error", ErrorObject); /* Check for errors */ if (PyErr_Occurred()) Py_FatalError("can't initialize the rrdtool module"); } /* * $Id: _rrdtoolmodule.c,v 1.14 2003/02/22 07:41:19 perky Exp $ * ex: ts=8 sts=4 et */ rrdtool-1.4.7/bindings/python/README0000644000175300017510000000134511014366674014146 00000000000000Based on Python-RRDtool 0.2.1 ----------------------------- The python-rrdtool provides a interface to rrdtool, the wonderful graphing and logging utility. This wrapper implementation has worked from the scratch (without SWIG), and it's under LGPL. This module have not documented yet. Please refer pydoc. Project ------- Homepage: http://www.nongnu.org/py-rrdtool/ Mailing Lists: CVS Checkins py-rrdtool-cvs@nongnu.org Users & Hackers py-rrdtool-users@nongnu.org Original Author --------------- Hye-Shik Chang Any comments, suggestions, and/or patches are very welcome. Thank you for using py-rrdtool! CHANGES ------- 2008-05-19 - tobi * rewrote the info method to conform to rrdtool info standard rrdtool-1.4.7/bindings/python/COPYING0000644000175300017510000006347610766723564014346 00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! rrdtool-1.4.7/bindings/python/AUTHORS0000644000175300017510000000004110766723564014336 00000000000000Hye-Shik Chang rrdtool-1.4.7/bindings/python/ACKNOWLEDGEMENT0000644000175300017510000000026610766723564015431 00000000000000ACKNOWLEDGMENT ============== This is a list of people who have made contributions to py-rrdtool. Matthew W. Samsonoff Brian E. Gallew rrdtool-1.4.7/bindings/perl-shared/0000777000175300017510000000000011707501552014227 500000000000000rrdtool-1.4.7/bindings/perl-shared/RRDs.xs0000644000175300017510000002444311705215351015335 00000000000000#ifdef __cplusplus extern "C" { #endif #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #ifdef __cplusplus } #endif /* * rrd_tool.h includes config.h, but at least on Ubuntu Breezy Badger * 5.10 with gcc 4.0.2, the C preprocessor picks up Perl's config.h * which is included from the Perl includes and never reads rrdtool's * config.h. Without including rrdtool's config.h, this module does * not compile, so include it here with an explicit path. * * Because rrdtool's config.h redefines VERSION which is originally * set via Perl's Makefile.PL and passed down to the C compiler's * command line, save the original value and reset it after the * includes. */ #define VERSION_SAVED VERSION #undef VERSION #ifndef WIN32 #include "../../rrd_config.h" #endif #include "../../src/rrd_tool.h" #undef VERSION #define VERSION VERSION_SAVED #undef VERSION_SAVED #define rrdcode(name) \ argv = (char **) malloc((items+1)*sizeof(char *));\ argv[0] = "dummy";\ for (i = 0; i < items; i++) { \ STRLEN len; \ char *handle= SvPV(ST(i),len);\ /* actually copy the data to make sure possible modifications \ on the argv data does not backfire into perl */ \ argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char)); \ strcpy(argv[i+1],handle); \ } \ rrd_clear_error();\ RETVAL=name(items+1,argv); \ for (i=0; i < items; i++) {\ free(argv[i+1]);\ } \ free(argv);\ \ if (rrd_test_error()) XSRETURN_UNDEF; #define hvs(VAL) hv_store_ent(hash, sv_2mortal(newSVpv(data->key,0)),VAL,0) #define rrdinfocode(name) \ /* prepare argument list */ \ argv = (char **) malloc((items+1)*sizeof(char *)); \ argv[0] = "dummy"; \ for (i = 0; i < items; i++) { \ STRLEN len; \ char *handle= SvPV(ST(i),len); \ /* actually copy the data to make sure possible modifications \ on the argv data does not backfire into perl */ \ argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char)); \ strcpy(argv[i+1],handle); \ } \ rrd_clear_error(); \ data=name(items+1, argv); \ for (i=0; i < items; i++) { \ free(argv[i+1]); \ } \ free(argv); \ if (rrd_test_error()) XSRETURN_UNDEF; \ hash = newHV(); \ save=data; \ while (data) { \ /* the newSV will get copied by hv so we create it as a mortal \ to make sure it does not keep hanging round after the fact */ \ switch (data->type) { \ case RD_I_VAL: \ if (isnan(data->value.u_val)) \ hvs(&PL_sv_undef); \ else \ hvs(newSVnv(data->value.u_val)); \ break; \ case RD_I_INT: \ hvs(newSViv(data->value.u_int)); \ break; \ case RD_I_CNT: \ hvs(newSViv(data->value.u_cnt)); \ break; \ case RD_I_STR: \ hvs(newSVpv(data->value.u_str,0)); \ break; \ case RD_I_BLO: \ hvs(newSVpv(data->value.u_blo.ptr,data->value.u_blo.size)); \ break; \ } \ data = data->next; \ } \ rrd_info_free(save); \ RETVAL = newRV_noinc((SV*)hash); /* * should not be needed if libc is linked (see ntmake.pl) #ifdef WIN32 #define free free #define malloc malloc #define realloc realloc #endif */ MODULE = RRDs PACKAGE = RRDs PREFIX = rrd_ BOOT: #ifdef MUST_DISABLE_SIGFPE signal(SIGFPE,SIG_IGN); #endif #ifdef MUST_DISABLE_FPMASK fpsetmask(0); #endif SV* rrd_error() CODE: if (! rrd_test_error()) XSRETURN_UNDEF; RETVAL = newSVpv(rrd_get_error(),0); OUTPUT: RETVAL int rrd_last(...) PROTOTYPE: @ PREINIT: int i; char **argv; CODE: rrdcode(rrd_last); OUTPUT: RETVAL int rrd_first(...) PROTOTYPE: @ PREINIT: int i; char **argv; CODE: rrdcode(rrd_first); OUTPUT: RETVAL int rrd_create(...) PROTOTYPE: @ PREINIT: int i; char **argv; CODE: rrdcode(rrd_create); RETVAL = 1; OUTPUT: RETVAL int rrd_update(...) PROTOTYPE: @ PREINIT: int i; char **argv; CODE: rrdcode(rrd_update); RETVAL = 1; OUTPUT: RETVAL int rrd_tune(...) PROTOTYPE: @ PREINIT: int i; char **argv; CODE: rrdcode(rrd_tune); RETVAL = 1; OUTPUT: RETVAL SV * rrd_graph(...) PROTOTYPE: @ PREINIT: char **calcpr=NULL; int i,xsize,ysize; double ymin,ymax; char **argv; AV *retar; PPCODE: argv = (char **) malloc((items+1)*sizeof(char *)); argv[0] = "dummy"; for (i = 0; i < items; i++) { STRLEN len; char *handle = SvPV(ST(i),len); /* actually copy the data to make sure possible modifications on the argv data does not backfire into perl */ argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char)); strcpy(argv[i+1],handle); } rrd_clear_error(); rrd_graph(items+1,argv,&calcpr,&xsize,&ysize,NULL,&ymin,&ymax); for (i=0; i < items; i++) { free(argv[i+1]); } free(argv); if (rrd_test_error()) { if(calcpr) for(i=0;calcpr[i];i++) rrd_freemem(calcpr[i]); XSRETURN_UNDEF; } retar=newAV(); if(calcpr){ for(i=0;calcpr[i];i++){ av_push(retar,newSVpv(calcpr[i],0)); rrd_freemem(calcpr[i]); } rrd_freemem(calcpr); } EXTEND(sp,4); PUSHs(sv_2mortal(newRV_noinc((SV*)retar))); PUSHs(sv_2mortal(newSViv(xsize))); PUSHs(sv_2mortal(newSViv(ysize))); SV * rrd_fetch(...) PROTOTYPE: @ PREINIT: time_t start,end; unsigned long step, ds_cnt,i,ii; rrd_value_t *data,*datai; char **argv; char **ds_namv; AV *retar,*line,*names; PPCODE: argv = (char **) malloc((items+1)*sizeof(char *)); argv[0] = "dummy"; for (i = 0; i < items; i++) { STRLEN len; char *handle= SvPV(ST(i),len); /* actually copy the data to make sure possible modifications on the argv data does not backfire into perl */ argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char)); strcpy(argv[i+1],handle); } rrd_clear_error(); rrd_fetch(items+1,argv,&start,&end,&step,&ds_cnt,&ds_namv,&data); for (i=0; i < items; i++) { free(argv[i+1]); } free(argv); if (rrd_test_error()) XSRETURN_UNDEF; /* convert the ds_namv into perl format */ names=newAV(); for (ii = 0; ii < ds_cnt; ii++){ av_push(names,newSVpv(ds_namv[ii],0)); rrd_freemem(ds_namv[ii]); } rrd_freemem(ds_namv); /* convert the data array into perl format */ datai=data; retar=newAV(); for (i = start+step; i <= end; i += step){ line = newAV(); for (ii = 0; ii < ds_cnt; ii++){ av_push(line,(isnan(*datai) ? &PL_sv_undef : newSVnv(*datai))); datai++; } av_push(retar,newRV_noinc((SV*)line)); } rrd_freemem(data); EXTEND(sp,5); PUSHs(sv_2mortal(newSViv(start+step))); PUSHs(sv_2mortal(newSViv(step))); PUSHs(sv_2mortal(newRV_noinc((SV*)names))); PUSHs(sv_2mortal(newRV_noinc((SV*)retar))); SV * rrd_times(start, end) char *start char *end PREINIT: rrd_time_value_t start_tv, end_tv; char *parsetime_error = NULL; time_t start_tmp, end_tmp; PPCODE: rrd_clear_error(); if ((parsetime_error = rrd_parsetime(start, &start_tv))) { rrd_set_error("start time: %s", parsetime_error); XSRETURN_UNDEF; } if ((parsetime_error = rrd_parsetime(end, &end_tv))) { rrd_set_error("end time: %s", parsetime_error); XSRETURN_UNDEF; } if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) { XSRETURN_UNDEF; } EXTEND(sp,2); PUSHs(sv_2mortal(newSVuv(start_tmp))); PUSHs(sv_2mortal(newSVuv(end_tmp))); int rrd_xport(...) PROTOTYPE: @ PREINIT: time_t start,end; int xsize; unsigned long step, col_cnt,row_cnt,i,ii; rrd_value_t *data,*ptr; char **argv,**legend_v; AV *retar,*line,*names; PPCODE: argv = (char **) malloc((items+1)*sizeof(char *)); argv[0] = "dummy"; for (i = 0; i < items; i++) { STRLEN len; char *handle = SvPV(ST(i),len); /* actually copy the data to make sure possible modifications on the argv data does not backfire into perl */ argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char)); strcpy(argv[i+1],handle); } rrd_clear_error(); rrd_xport(items+1,argv,&xsize,&start,&end,&step,&col_cnt,&legend_v,&data); for (i=0; i < items; i++) { free(argv[i+1]); } free(argv); if (rrd_test_error()) XSRETURN_UNDEF; /* convert the legend_v into perl format */ names=newAV(); for (ii = 0; ii < col_cnt; ii++){ av_push(names,newSVpv(legend_v[ii],0)); rrd_freemem(legend_v[ii]); } rrd_freemem(legend_v); /* convert the data array into perl format */ ptr=data; retar=newAV(); for (i = start+step; i <= end; i += step){ line = newAV(); for (ii = 0; ii < col_cnt; ii++){ av_push(line,(isnan(*ptr) ? &PL_sv_undef : newSVnv(*ptr))); ptr++; } av_push(retar,newRV_noinc((SV*)line)); } rrd_freemem(data); EXTEND(sp,7); PUSHs(sv_2mortal(newSViv(start+step))); PUSHs(sv_2mortal(newSViv(end))); PUSHs(sv_2mortal(newSViv(step))); PUSHs(sv_2mortal(newSViv(col_cnt))); PUSHs(sv_2mortal(newRV_noinc((SV*)names))); PUSHs(sv_2mortal(newRV_noinc((SV*)retar))); SV* rrd_info(...) PROTOTYPE: @ PREINIT: rrd_info_t *data,*save; int i; char **argv; HV *hash; CODE: rrdinfocode(rrd_info); OUTPUT: RETVAL SV* rrd_updatev(...) PROTOTYPE: @ PREINIT: rrd_info_t *data,*save; int i; char **argv; HV *hash; CODE: rrdinfocode(rrd_update_v); OUTPUT: RETVAL SV* rrd_graphv(...) PROTOTYPE: @ PREINIT: rrd_info_t *data,*save; int i; char **argv; HV *hash; CODE: rrdinfocode(rrd_graph_v); OUTPUT: RETVAL int rrd_dump(...) PROTOTYPE: @ PREINIT: int i; char **argv; CODE: rrdcode(rrd_dump); RETVAL = 1; OUTPUT: RETVAL int rrd_restore(...) PROTOTYPE: @ PREINIT: int i; char **argv; CODE: rrdcode(rrd_restore); RETVAL = 1; OUTPUT: RETVAL #ifndef WIN32 int rrd_flushcached(...) PROTOTYPE: @ PREINIT: int i; char **argv; CODE: rrdcode(rrd_flushcached); OUTPUT: RETVAL #endif rrdtool-1.4.7/bindings/perl-shared/RRDs.pm0000644000175300017510000001021211707501460015305 00000000000000package RRDs; use strict; use vars qw(@ISA $VERSION); @ISA = qw(DynaLoader); require DynaLoader; $VERSION=1.4007; bootstrap RRDs $VERSION; 1; __END__ =head1 NAME RRDs - Access RRDtool as a shared module =head1 SYNOPSIS use RRDs; RRDs::error RRDs::last ... RRDs::info ... RRDs::create ... RRDs::update ... RRDs::updatev ... RRDs::graph ... RRDs::fetch ... RRDs::tune ... RRDs::times(start, end) RRDs::dump ... RRDs::restore ... RRDs::flushcached ... =head1 DESCRIPTION =head2 Calling Sequence 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 command line 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. The RRDs::times function takes two parameters: a "start" and "end" time. These should be specified in the B format used by RRDtool. See the B documentation for a detailed explanation on how to specify time. =head2 Error Handling 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; =head2 Return Values The functions RRDs::last, RRDs::graph, RRDs::info, RRDs::fetch and RRDs::times return their findings. B returns a single INTEGER representing the last update time. $lastupdate = RRDs::last ... B returns an ARRAY containing the x-size and y-size of the created image and a pointer to an array with the results of the PRINT arguments. ($result_arr,$xsize,$ysize) = RRDs::graph ... print "Imagesize: ${xsize}x${ysize}\n"; print "Averages: ", (join ", ", @$averages); B 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"; } B takes the same parameters as B but it returns a pointer to hash. The hash returned contains meta information about the graph. Like its size as well as the position of the graph area on the image. When calling with and empty filename than the contents of the graph will be returned in the hash as well (key 'image'). B also returns a pointer to hash. The keys of the hash are concatenated strings of a timestamp, RRA index, and data source name for each consolidated data point (CDP) written to disk as a result of the current update call. The hash values are CDP values. B 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"; for my $line (@$data) { print " ", scalar localtime($start), " ($start) "; $start += $step; for my $val (@$line) { printf "%12.1f ", $val; } print "\n"; } B returns two integers which are the number of seconds since epoch (1970-01-01) for the supplied "start" and "end" arguments, respectively. See the examples directory for more ways to use this extension. =head1 NOTE If you are manipulating the TZ variable you should also call the POSIX function L to initialize all internal state of the library for properly operating in the timezone of your choice. use POSIX qw(tzset); $ENV{TZ} = 'CET'; POSIX::tzset(); =head1 AUTHOR Tobias Oetiker Etobi@oetiker.chE =cut rrdtool-1.4.7/bindings/perl-shared/Makefile.PL0000644000175300017510000000506511672415451016126 00000000000000use ExtUtils::MakeMaker; use Config; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. if (($Config{'osname'} eq 'MSWin32' && $ENV{'OSTYPE'} eq '')) { WriteMakefile( 'NAME' => 'RRDs', 'VERSION_FROM' => 'RRDs.pm', 'DEFINE' => "-DPERLPATCHLEVEL=$Config{PATCHLEVEL} -D_CRT_SECURE_NO_WARNINGS -DWIN32", 'INC' => '-I../../src/ "-IC:/Perl/lib/CORE" -I"C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include" -I"C:\Program Files\Microsoft SDKs\Windows\v6.0A\Include"', 'LDDLFLAGS' => '-dll -nologo -opt:ref,icf -ltcg -libpath:"C:\Perl\lib\CORE" -machine:X86', 'LDFLAGS' => '-nologo -opt:ref,icf -ltcg -libpath:"C:\Perl\lib\CORE" -machine:X86', 'OPTIMIZE' => '-O2 -MD', 'LIBS' => '"..\..\win32\Release\rrdlib.lib" "..\..\win32\Release" "C:\Perl\lib\CORE\perl514.lib" -L../../contrib/lib -L"C:\Program Files\Microsoft SDKs\Windows\v6.0A\lib" -L"C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\lib" -L"C:\Perl\lib\CORE"', 'realclean' => {FILES => 't/demo?.rrd t/demo?.png' }, ($] ge '5.005') ? ( 'AUTHOR' => 'Tobias Oetiker (tobi@oetiker.ch)', 'ABSTRACT' => 'Round Robin Database Tool', ) : () ); }else{ # if the last argument when calling Makefile.PL is RPATH=/... and ... is the # path to librrd.so then the Makefile will be written such that RRDs.so knows # where to find librrd.so later on ... my $R=""; if ($ARGV[-1] =~ /RPATH=(\S+)/){ pop @ARGV; my $rp = $1; for ($^O){ /linux/ && do{ $R = "-Wl,--rpath -Wl,$rp"}; /hpux/ && do{ $R = "+b$rp"}; /solaris/ && do{ $R = "-R$rp"}; /bsd/ && do{ $R = "-R$rp"}; /aix/ && do{ $R = "-blibpath:$rp"}; } } # darwin works without this because librrd contains its # install_name which will includes the final location of the # library after it is installed. This install_name gets transfered # to the perl shared object. my $librrd; if ($^O eq 'darwin'){ $librrd = '-lrrd'; } else { $librrd = "-L../../src/.libs/ $R -lrrd"; } WriteMakefile( 'NAME' => 'RRDs', 'VERSION_FROM' => 'RRDs.pm', # finds $VERSION 'DEFINE' => "-DPERLPATCHLEVEL=$Config{PATCHLEVEL}", 'INC' => '-I../../src', # Perl will figure out which one is valid #'dynamic_lib' => {'OTHERLDFLAGS' => "$librrd -lm"}, 'depend' => {'RRDs.c' => "../../src/librrd.la"}, 'LDFROM' => '$(OBJECT) '.$librrd, 'realclean' => {FILES => 't/demo?.rrd t/demo?.png' }, ($^O eq 'darwin') ? ( 'LDDLFLAGS' => "-L../../src/.libs/ $Config{lddlflags}" ) : () ); } rrdtool-1.4.7/bindings/perl-shared/README0000644000175300017510000000107211672415451015026 00000000000000These are the Perl bindings for rrdtool as a shared library. To compile do the following: perl Makefile.PL make test For Windows Users, make sure you have the following requirements: - ActiveState Perl (32bit version Only) - Microsoft Visual C++ - The following binaries in your path: mt.exe, nmake.exe, link.exe, perl.exe - Make the project rrdlib.vcproj in Release mode to create rrdlib.lib To build: perl Makefile.PL nmake To Install: nmake install NOTE: If using anything other than Visual C++ 9, edit Makefile.PL and modify your paths appropriately. rrdtool-1.4.7/bindings/perl-shared/MANIFEST0000644000175300017510000000006511672415451015300 00000000000000MANIFEST README Makefile.PL RRDs.pm RRDs.xs t/base.t rrdtool-1.4.7/bindings/perl-shared/ntmake-build0000644000175300017510000000227411154675636016462 00000000000000use ExtUtils::MakeMaker; use Config; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. # Run VCVARS32.BAT before generating makefile/compiling. WriteMakefile( 'NAME' => 'RRDs', 'VERSION_FROM' => 'RRDs.pm', # 'DEFINE' => "-DPERLPATCHLEVEL=$Config{PATCHLEVEL}", # keep compatible w/ ActiveState 5xx builds 'DEFINE' => "-DPERLPATCHLEVEL=5", 'INC' => '-I../../src/ "-I/Program Files/GnuWin32/include"', # Since we are now using GnuWin32 libraries dynamically (instead of static # complile with code redistributed with rrdtool), use /MD instead of /MT. # Yes, this means we need msvcrt.dll but GnuWin32 dlls already require it # and it is available on most versions of Windows. 'OPTIMIZE' => '-O2 -MD', 'LIBS' => '../../src/release/rrd.lib "/Program Files/GnuWin32/lib/libart_lgpl.lib" "/Program Files/GnuWin32/lib/libz.lib" "/Program Files/GnuWin32/lib/libpng.lib" "/Program Files/GnuWin32/lib/libfreetype.lib"', 'realclean' => {FILES => 't/demo?.rrd t/demo?.png' }, ($] ge '5.005') ? ( 'AUTHOR' => 'Tobias Oetiker (tobi@oetiker.ch)', 'ABSTRACT' => 'Round Robin Database Tool', ) : () ); rrdtool-1.4.7/bindings/perl-shared/t/0000777000175300017510000000000011707501552014472 500000000000000rrdtool-1.4.7/bindings/perl-shared/t/base.t0000755000175300017510000001047610766723564015535 00000000000000#! /usr/bin/perl BEGIN { $| = 1; print "1..7\n"; } END { print "not ok 1\n" unless $loaded; unlink "demo.rrd"; } sub ok { my($what, $result) = @_ ; $ok_count++; print "not " unless $result; print "ok $ok_count $what\n"; } use strict; use vars qw(@ISA $loaded); use RRDs; $loaded = 1; my $ok_count = 1; ok("loading",1); ######################### End of black magic. my $STEP = 100; my $RUNS = 500; my $GRUNS = 4; my $RRD1 = "demo1.rrd"; my $RRD2 = "demo2.rrd"; my $PNG1 = "demo1.png"; my $PNG2 = "demo2.png"; my $time = 30*int(time/30); my $START = $time-$RUNS*$STEP; my @options = ("-b", $START, "-s", $STEP, "DS:a:GAUGE:2000:U:U", "DS:b:GAUGE:200:U:U", "DS:c:GAUGE:200:U:U", "DS:d:GAUGE:200:U:U", "DS:e:DERIVE:200:U:U", "RRA:AVERAGE:0.5:1:5000", "RRA:AVERAGE:0.5:10:500"); print "* Creating RRD $RRD1 starting at $time.\n\n"; RRDs::create $RRD1, @options; my $ERROR = RRDs::error; ok("create 1", !$ERROR); # 2 if ($ERROR) { die "$0: unable to create `$RRD1': $ERROR\n"; } print "* Creating RRD $RRD2 starting at $time.\n\n"; RRDs::create $RRD2, @options; $ERROR= RRDs::error; ok("create 2",!$ERROR); # 3 if ($ERROR) { die "$0: unable to create `$RRD2': $ERROR\n"; } my $last = RRDs::last $RRD1; if ($ERROR = RRDs::error) { die "$0: unable to get last `$RRD1': $ERROR\n"; } ok("last 1", $last == $START); # 4 $last = RRDs::last $RRD2; if ($ERROR = RRDs::error) { die "$0: unable to get last `$RRD2': $ERROR\n"; } ok("last 2", $last == $START); # 5 print "* Filling $RRD1 and $RRD2 with $RUNS*5 values. One moment please ...\n"; print "* If you are running over NFS this will take *MUCH* longer\n\n"; srand(int($time / 100)); @options = (); my $counter = 1e7; for (my $t=$START+1; $t<$START+$STEP*$RUNS; $t+=$STEP+int((rand()-0.5)*7)){ $counter += int(2500*sin($t/2000)*$STEP); my $data = (1000+500*sin($t/1000)).":". (1000+900*sin($t/2330)).":". (2000*cos($t/1550)).":". (3220*sin($t/3420)).":$counter"; push(@options, "$t:$data"); RRDs::update $RRD1, "$t:$data"; if ($ERROR = RRDs::error) { warn "$0: unable to update `$RRD1': $ERROR\n"; } } ok("update 1",!$ERROR); # 3 RRDs::update $RRD2, @options; ok("update 2",!$ERROR); # 3 if ($ERROR = RRDs::error) { die "$0: unable to update `$RRD2': $ERROR\n"; } print "* Creating $GRUNS graphs: $PNG1 & $PNG2\n\n"; my $now = $time; for (my $i=0;$i<$GRUNS;$i++) { my @rrd_pngs = ($RRD1, $PNG1, $RRD2, $PNG2); while (@rrd_pngs) { my $RRD = shift(@rrd_pngs); my $PNG = shift(@rrd_pngs); my ($graphret,$xs,$ys) = RRDs::graph $PNG, "--title", 'Test GRAPH', "--vertical-label", 'Dummy Units', "--start", (-$RUNS*$STEP), "DEF:alpha=$RRD:a:AVERAGE", "DEF:beta=$RRD:b:AVERAGE", "DEF:gamma=$RRD:c:AVERAGE", "DEF:delta=$RRD:d:AVERAGE", "DEF:epsilon=$RRD:e:AVERAGE", "CDEF:calc=alpha,beta,+,2,/", "AREA:alpha#0022e9:Short", "STACK:beta#00b871:Demo Text", "LINE1:gamma#ff0000:Line 1", "LINE2:delta#888800:Line 2", "LINE3:calc#00ff44:Line 3", "LINE3:epsilon#000000:Line 4", "HRULE:1500#ff8800:Horizontal Line at 1500", "PRINT:alpha:AVERAGE:Average Alpha %1.2lf", "PRINT:alpha:MIN:Min Alpha %1.2lf %s", "PRINT:alpha:MIN:Min Alpha %1.2lf", "PRINT:alpha:MAX:Max Alpha %1.2lf", "GPRINT:calc:AVERAGE:Average calc %1.2lf %s", "GPRINT:calc:AVERAGE:Average calc %1.2lf", "GPRINT:calc:MAX:Max calc %1.2lf", "GPRINT:calc:MIN:Min calc %1.2lf", "VRULE:".($now-3600)."#008877:60 Minutes ago", "VRULE:".($now-7200)."#008877:120 Minutes ago"; if ($ERROR = RRDs::error) { print "ERROR: $ERROR\n"; } else { print "Image Size: ${xs}x${ys}\n"; print "Graph Return:\n",(join "\n", @$graphret),"\n\n"; } } } my ($start,$step,$names,$array) = RRDs::fetch $RRD1, "AVERAGE"; $ERROR = RRDs::error; print "ERROR: $ERROR\n" if $ERROR ; print "start=$start, step=$step\n"; print " "; map {printf("%12s",$_)} @$names ; foreach my $line (@$array){ print "".localtime($start)," "; $start += $step; foreach my $val (@$line) { if (not defined $val){ printf "%12s", "UNKNOWN"; } else { printf "%12.1f", $val; } } print "\n"; } rrdtool-1.4.7/bindings/perl-piped/0000777000175300017510000000000011707501552014062 500000000000000rrdtool-1.4.7/bindings/perl-piped/RRDp.pm0000644000175300017510000001257111707501460015147 00000000000000package RRDp; =head1 NAME RRDp - Attach RRDtool from within a perl script via a set of pipes; =head1 SYNOPSIS use B B I B I $answer = B $status = B B<$RRDp::user>, B<$RRDp::sys>, B<$RRDp::real>, B<$RRDp::error_mode>, B<$RRDp::error> =head1 DESCRIPTION With this module you can safely communicate with the RRDtool. After every B you have to issue an B command to get Bs 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, B will return an undefined variable. If you import the PERFORMANCE variables into your namespace, you can access RRDtool's internal performance measurements. =over 8 =item use B Load the RRDp::pipe module. =item B I start RRDtool. The argument must be the path to the RRDtool executable =item B I pass commands on to RRDtool. Check the RRDtool documentation for more info on the RRDtool commands. B: Due to design limitations, B does not support the C command - use C instead. =item $answer = B read RRDtool's 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. =item $status = B terminates RRDtool and returns RRDtool's status ... =item B<$RRDp::user>, B<$RRDp::sys>, B<$RRDp::real> 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. =item B<$RRDp::error_mode> and B<$RRDp::error> If you set the variable $RRDp::error_mode to the value 'catch' before you run RRDp::read a potential ERROR message will not cause the program to abort but will be returned in this variable. If no error occurs the variable will be empty. $RRDp::error_mode = 'catch'; RRDp::cmd qw(info file.rrd); print $RRDp::error if $RRDp::error; =back =head1 EXAMPLE 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); =head1 SEE ALSO For more information on how to use RRDtool, check the manpages. =head1 AUTHOR Tobias Oetiker =cut #' this is to make cperl.el happy use strict; use Fcntl; use Carp; use IO::Handle; use IPC::Open2; use vars qw($Sequence $RRDpid $VERSION); my $Sequence; my $RRDpid; # Prototypes sub start ($); sub cmd (@); sub end (); sub read (); $VERSION=1.4007; sub start ($){ croak "rrdtool is already running" if defined $Sequence; $Sequence = 'S'; my $rrdtool = shift @_; $RRDpid = open2 \*RRDreadHand,\*RRDwriteHand, $rrdtool,"-" or croak "Can't Start rrdtool: $!"; RRDwriteHand->autoflush(); #flush after every write fcntl RRDreadHand, F_SETFL,O_NONBLOCK|O_NDELAY; #make readhandle NON BLOCKING return $RRDpid; } sub read () { croak "RRDp::read can only be called after RRDp::cmd" unless $Sequence eq 'C'; $RRDp::error = undef; $Sequence = 'R'; my $inmask = 0; my $srbuf; my $minibuf; my $buffer; my $nfound; my $timeleft; vec($inmask,fileno(RRDreadHand),1) = 1; # setup select mask for Reader while (1) { my $rout; $nfound = select($rout=$inmask,undef,undef,2); if ($nfound == 0 ) { # here, we could do something sensible ... next; } sysread(RRDreadHand,$srbuf,4096); $minibuf .= $srbuf; while ($minibuf =~ s|^(.+?)\n||s) { my $line = $1; # print $line,"\n"; $RRDp::error = undef; if ($line =~ m|^ERROR|) { $RRDp::error_mode eq 'catch' ? $RRDp::error = $line : croak $line; $RRDp::sys = undef; $RRDp::user = undef; $RRDp::real = undef; return undef; } elsif ($line =~ m|^OK(?: u:([\d\.]+) s:([\d\.]+) r:([\d\.]+))?|){ ($RRDp::sys,$RRDp::user,$RRDp::real)=($1,$2,$3); return \$buffer; } else { $buffer .= $line. "\n"; } } } } sub cmd (@){ croak "RRDp::cmd can only be called after RRDp::read or RRDp::start" unless $Sequence eq 'R' or $Sequence eq 'S'; $Sequence = 'C'; my $cmd = join " ", @_; if ($Sequence ne 'S') { } $cmd =~ s/\n/ /gs; $cmd =~ s/\s/ /gs; # The generated graphs aren't necessarily terminated by a newline, # causing RRDp::read() to wait for a line matching '^OK' forever. if ($cmd =~ m/^\s*graph\s+-\s+/) { croak "RRDp does not support the 'graph -' command - " . "use 'graphv -' instead"; } print RRDwriteHand "$cmd\n"; } sub end (){ croak "RRDp::end can only be called after RRDp::start" unless $Sequence; close RRDwriteHand; close RRDreadHand; $Sequence = undef; waitpid $RRDpid,0; return $? } 1; rrdtool-1.4.7/bindings/perl-piped/Makefile.PL0000644000175300017510000000050610766723564015766 00000000000000use ExtUtils::MakeMaker; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. WriteMakefile( 'NAME' => 'RRDp', 'VERSION' => '0.99.0', # finds $VERSION 'linkext' => {LINKTYPE => ''}, 'dist' => {COMPRESS=>'gzip', SUFFIX=>'gz'}, ); rrdtool-1.4.7/bindings/perl-piped/README0000644000175300017510000000015510766723564014674 00000000000000This is a Perl module for using RRDtool process via a set of pipes. perl Makefile.PL make test make install rrdtool-1.4.7/bindings/perl-piped/MANIFEST0000644000175300017510000000005510766723564015144 00000000000000MANIFEST README Makefile.PL RRDp.pm t/base.t rrdtool-1.4.7/bindings/perl-piped/t/0000777000175300017510000000000011707501552014325 500000000000000rrdtool-1.4.7/bindings/perl-piped/t/base.t0000755000175300017510000000137710766723564015370 00000000000000#! /usr/bin/perl # this exercises just the perl module .. not RRDtool as such ... BEGIN { $| = 1; print "1..5\n"; } END { print "not ok 1\n" unless $loaded; unlink "demo.rrd"; } sub ok { $ok_count++; my($what, $result) = @_ ; print "not " unless $result; print "ok $ok_count $what\n"; } use RRDp; $loaded = 1; $ok_count = 1; print "ok 1 module load\n"; ok("RRDp::start", RRDp::start "../../src/rrdtool" > 0); $now=time(); RRDp::cmd qw(create demo.rrd --start ), $now, qw(--step 100 ), qw( DS:in:GAUGE:100:U:U RRA:AVERAGE:0.5:1:10 ); $answer = RRDp::read; ok("RRDp::cmd", -s "demo.rrd" ); RRDp::cmd qw(last demo.rrd); $answer = RRDp::read; ok("RRDp::read", $$answer =~ /$now/); $status = RRDp::end; ok("RRDp::end", $status == 0); rrdtool-1.4.7/doc/0000777000175300017510000000000011707501552010771 500000000000000rrdtool-1.4.7/doc/rrdlua.pod0000644000175300017510000001544611072316217012711 00000000000000=head1 NAME RRDLua - Lua binding for RRDTool =head1 SYNOPSIS require 'rrd' rrd.create(...) rrd.dump(...) rrd.fetch(...) rrd.first(...) rrd.graph(...) rrd.graphv(...) rrd.info(...) rrd.last(...) rrd.resize(...) rrd.restore(...) rrd.tune(...) rrd.update(...) rrd.updatev(...) =head1 DESCRIPTION =head2 Calling Sequence This module accesses RRDtool functionality directly from within Lua. The arguments to the functions listed in the SYNOPSIS are explained in the regular RRDtool documentation. The command-line call rrdtool update mydemo.rrd --template in:out N:12:13 gets turned into rrd.update ("mydemo.rrd", "--template", "in:out", "N:12:13") Note that --template=in:out is also valid. =head2 Using with Lua 5.1 Start your programs with: --------------------------------------------------------------- package.cpath = '/usr/local/rrdtool-1.3.2/lib/lua/5.1/?.so;' .. package.cpath require 'rrd' --------------------------------------------------------------- OBS: If you configured with --enable-lua-site-install, you don't need to set package.cpath like above. =head2 Using with Lua 5.0 The Lua binding for RRDtool needs the Lua module compat-5.1 to work with Lua 5.0. Some Linux distros, like Ubuntu gutsy and hardy, have it already integrated in Lua 5.0 -dev packages, so you just have to require it: require 'compat-5.1' For other platforms, the compat-5.1 module that comes with this binding will be installed for you in the same dir where RRDtool was installed, under the subdir .../lib/lua/5.0. In this case, you must tell your Lua programs where to find it by changing the Lua var LUA_PATH: -- compat-5.1.lua is only necessary for Lua 5.0 ---------------- -- try only compat-5.1 installed with RRDtool package local original_LUA_PATH = LUA_PATH LUA_PATH = '/usr/local/rrdtool-1.3.2/lib/lua/5.0/?.lua' require 'compat-5.1' LUA_PATH = original_LUA_PATH original_LUA_PATH = nil --- end of code to require compat-5.1 --------------------------- Now we can require the rrd module in the same way we did for 5.1 above: --------------------------------------------------------------- package.cpath = '/usr/local/rrdtool-1.3.2/lib/lua/5.0/?.so;' .. package.cpath require 'rrd' --------------------------------------------------------------- =head2 Error Handling The Lua RRDTool module functions will abort your program with a stack traceback when they can not make sense out of the arguments you fed them. However, you can capture and handle the errors yourself, instead of just letting the program abort, by calling the module functions through Lua protected calls - 'pcall' or 'xpcall'. Ex: program t.lua --- compat-5.1.lua is only necessary for Lua 5.0 ---------------- -- uncomment below if your distro has not compat-5.1 -- original_LUA_PATH = LUA_PATH -- try only compat-5.1.lua installed with RRDtool package -- LUA_PATH = '/usr/local/rrdtool-1.3.2/lib/lua/5.0/?.lua' -- here we use a protected call to require compat-5.1 local r = pcall(require, 'compat-5.1') if not r then print('** could not load compat-5.1.lua') os.exit(1) end -- uncomment below if your distro has not compat-5.1 -- LUA_PATH = original_LUA_PATH -- original_LUA_PATH = nil --- end of code to require compat-5.1 --------------------------- -- If the Lua RRDTool module was installed together with RRDTool, -- in /usr/local/rrdtool-1.3.2/lib/lua/5.0, package.cpath must be -- set accordingly so that 'require' can find the module: package.cpath = '/usr/local/rrdtool-1.3.2/lib/lua/5.0/?.so;' .. package.cpath local rrd = require 'rrd' rrd.update ("mydemo.rrd","N:12:13") If we execute the program above we'll get: $ lua t.lua lua: t.lua:27: opening 'mydemo.rrd': No such file or directory stack traceback: [C]: in function `update' t.lua:27: in main chunk [C]: ? =head2 Return Values The functions rrd.first, rrd.last, rrd.graph, rrd.info and rrd.fetch return their findings. B returns a single INTEGER representing the timestamp of the first data sample in an RRA within an RRD file. Example returning the first timestamp of the third RRA (index 2): local firstdate = rrd.first('example.rrd', '--rraindex', 2) B returns a single INTEGER representing the last update time. local lastupdate = rrd.last('example.rrd') B returns the x-size and y-size of the created image and a table with the results of the PRINT arguments. local xsize, ysize, averages = rrd.graph ... print(string.format("Image size: %dx%d", xsize, ysize) print("Averages: ", table.concat(averages, ', ')) B returns a table where the keys and the values represent property names and property values of the RRD. local info = rrd.info("test.rrd") for key, value in pairs(info) do print(key, ' = ', value) end B takes the same parameters as rrd.graph but it returns a table only. The table returned contains meta information about the graph, like its size as well as the position of the graph area on the image. When called with and empty filename, the contents of the graph will be returned in the table as well (key 'image'). B also returns a table. The keys of the table are strings formed by the concatenation of timestamp, RRA index and data source name for each consolidated data point (CDP) written to disk as a result of the current update call. The key values are CDP values. B is the most complex of the pack regarding return values. It returns 5 values: the initial timestamp, the step, two parallel arrays containing the data source names and their data points respectively, and the final timestamp. --require compat-5.1 if necessary package.cpath = '/usr/local/rrdtool-1.3.2/lib/lua/5.0/?.so;' .. package.cpath local rrd = require "rrd" local first, last = rrd.first("test.rrd"), rrd.last("test.rrd") local start, step, names, data = rrd.fetch("test.rrd", "--start", first, "--end", last, "AVERAGE") io.write(string.format("Start: %s (%d)\n", os.date("%c", start),start)) io.write("Step size: ", step, " seconds\n") io.write("DS names: ", table.concat(names, ', '), "\n") io.write("Data points: ", #data[1], "\n") io.write("Data:\n") for i,dp in ipairs(data) do io.write(os.date("%t", start), " (", start, "): ") start = start + step for j,v in ipairs(dp) do io.write(v, " ") end io.write("\n") end =head1 AUTHOR Fidelis Assis Efidelis@pobox.comE rrdtool-1.4.7/doc/rrdgraph_libdbi.pod0000644000175300017510000001437711265303053014536 00000000000000=head1 NAME rrdgraph_libdbi - fetching data for graphing in rrdtool graph via libdbi =head1 SYNOPSIS ErrdfileE = Blibdbi driverE/Edriver-option-nameE=Edriver-option-valueE/...[/rrdminstepsize=EstepsizeE][/rrdfillmissing=Efill missing n samplesE]//EtableE/Eunixtimestamp columnE/Edata value columnE[/derive]/Ewhere clause 1E/.../Ewhere clause nE> =head1 DESCRIPTION This pseudo-rrd-filename defines a sql datasource: =over 8 =item B magic cookie-prefix for a libdbi type datasource =item Blibdbi driverE> which libdbi driver to use (e.g: mysql) =item Bdriver-option-nameE>=Bdriver-option-valueE> defines the parameters that are required to connect to the database with the given libdbi driver (These drivers are libdbi dependent - for details please look at the driver documentation of libdbi!) =item B=Bminimum step sizeE> defines the minimum number of the step-length used for graphing (default: 300 seconds) =item B=Bfill missing stepsE> defines the number of steps to fill with the last value to avoid NaN boxes due to data-insertation jitter (default: 0 steps) =item BtableE> defines the table from which to fetch the resultset. If there is a need to fetch data from several tables, these tables can be defined by separating the tablenames with a "+" hex-type-encoding via %xx are translated to the actual value, use %% to use % =item B[*]unixtimestamp columnE> defines the column of EtableE which contains the unix-timestamp - if this is a DATETIME field in the database, then prefix with leading '*' hex-type-encoding via %xx are translated to the actual value, use %% to use % =item Bdata value columnE> defines the column of EtableE which contains the value column, which should be graphed hex-type-encoding via %xx are translated to the actual value, use %% to use % =item B defines that the data value used should be the delta of the 2 consecutive values (to simulate COUNTER or DERIVE type datasources) =item Bwhere clause(s)E> defines one (ore more) where clauses that are joined with AND to filter the entries in the table hex-type-encoding via %xx are translated to the actual value, use %% to use % =back the returned value column-names, which can be used as ds-names, are: =over 8 =item B, B, B, B and B are returned to be used as ds-names in your DS definition. The reason for using this is that if the consolidation function is used for min/avg and max, then the engine is used several times. And this results in the same SQL Statements used several times =back =head1 EXAMPLES Here an example of a table in a MySQL database: DB connect information dbhost=127.0.0.1 user=rrd password=secret database=rrd here the table: CREATE TABLE RRDValue ( RRDKeyID bigint(20) NOT NULL, UnixTimeStamp int(11) NOT NULL, value double default NOT NULL, PRIMARY KEY (RRDKeyID,UnixTimeStamp) ); and the RRDKeyID we want to graph for is: 1141942900757789274 The pseudo rrd-filename to access this is: "sql//mysql/host=127.0.0.1/dbname=rrd/username=rrd/password=secret//RRDValue/UnixTimeStamp/value/RRDKeyID=1141464142203608274" To illustrate this here a command to create a graph that contains the actual values. DS_BASE="sql//mysql/host=127.0.0.1/dbname=rrd/username=rrd/password=passwd//RRDValue/UnixTimeStamp/value/RRDKeyID=1141942900757789274" rrdtool graph test.png --imgformat=PNG --start=-1day --end=+3hours --width=1000 --height=600 \ "DEF:min=$DS_BASE:min:AVERAGE" \ "LINE1:min#FF0000:value" \ "DEF:avg=$DS_BASE:avg:AVERAGE" \ "LINE1:avg#00FF00:average" \ "DEF:max=$DS_BASE:max:AVERAGE" \ "LINE1:max#FF0000:max" \ "DEF:sigma=$DS_BASE:sigma:AVERAGE" \ "CDEF:upper=avg,4,sigma,*,+" \ "LINE1:upper#0000FF:+4 sigma" \ "CDEF:lower=avg,4,sigma,*,-" \ "LINE1:lower#0000FF:-4 sigma" =head1 NOTES * Naturally you can also use any other kind of driver that libdbi supports - e.g postgres, ... * From the way the data source is joined, it should also be possible to do joins over different tables (separate tables with "," in table and add in the WHERE Clauses the table equal joins. This has not been tested!!!) * It should also be relatively simple to add to the database using the same data source string. This has not been implemented... * The aggregation functions are ignored and several data columns are used instead to avoid querying the same SQL several times when minimum, average and maximum are needed for graphing... * for DB efficiency you should think of having 2 tables, one containing historic values and the other containing the latest data. This second table should be kept small to allow for the least ammount of blocking SQL statements. Whith mysql you can even use myisam table-type for the first and InnoDB for the second. This is especially interresting as with tables with +100M rows myisam is much smaller then InnoDB. * To debug the SQL statements set the environment variable RRDDEBUGSQL and the actual SQL statements and the timing is printed to stderr. =head1 BUGS * at least on Linux please make sure that the libdbi driver is explicitly linked against libdbi.so.0 check via ldd /usr/lib/dbd/libmysql.so, that there is a line with libdbi.so.0. otherwise at least the perl module RRDs will fail because the dynamic linker can not find some symbols from libdbi.so. (this only happens when the libdbi driver is actually used the first time!) This is KNOWN to be the case with RHEL4 and FC4 and FC5! (But actually this is a bug with libdbi make files!) * at least version 0.8.1 of libdbiexhibits a bug with BINARY fields (shorttext,text,mediumtext,longtext and possibly also BINARY and BLOB fields), that can result in coredumps of rrdtool. The tool will tell you on stderr if this occures, so that you know what may be the reason. If you are not experiencing these coredumps, then set the environment variable RRD_NO_LIBDBI_BUG_WARNING, and then the message will not get shown. =head1 AUTHOR Martin Sperl rrdtool-1.4.7/doc/rrdtool-xport.dtd0000644000175300017510000000160410766723564014257 00000000000000 rrdtool-1.4.7/doc/rrdtool-dump.dtd0000644000175300017510000000230710766723564014051 00000000000000 rrdtool-1.4.7/doc/librrd.txt0000644000175300017510000001425411707501552012732 00000000000000librrd(3) rrdtool librrd(3) NNAAMMEE librrd - RRD library functions DDEESSCCRRIIPPTTIIOONN lliibbrrrrdd contains most of the functionality in RRRRDDTTooooll. The command line utilities and language bindings are often just wrappers around the code contained in lliibbrrrrdd. This manual page documents the lliibbrrrrdd API. NNOOTTEE:: This document is a work in progress, and should be considered incomplete as long as this warning persists. For more information about the lliibbrrrrdd functions, always consult the source code. CCOORREE FFUUNNCCTTIIOONNSS rrrrdd__dduummpp__ccbb__rr((cchhaarr **ffiilleennaammee,, iinntt oopptt__hheeaaddeerr,, rrrrdd__oouuttppuutt__ccaallllbbaacckk__tt ccbb,, vvooiidd **uusseerr)) In some situations it is necessary to get the output of "rrd_dump" without writing it to a file or the standard output. In such cases an application can ask rrrrdd__dduummpp__ccbb__rr to call an user-defined function each time there is output to be stored somewhere. This can be used, to e.g. directly feed an XML parser with the dumped output or transfer the resulting string in memory. The arguments for rrrrdd__dduummpp__ccbb__rr are the same as for rrrrdd__dduummpp__oopptt__rr except that the output filename parameter is replaced by the user- defined callback function and an additional parameter for the callback function that is passed untouched, i.e. to store information about the callback state needed for the user-defined callback to function properly. Recent versions of rrrrdd__dduummpp__oopptt__rr internally use this callback mechanism to write their output to the file provided by the user. size_t rrd_dump_opt_cb_fileout( const void *data, size_t len, void *user) { return fwrite(data, 1, len, (FILE *)user); } The associated call for rrrrdd__dduummpp__ccbb__rr looks like res = rrd_dump_cb_r(filename, opt_header, rrd_dump_opt_cb_fileout, (void *)out_file); where the last parameter specifies the file handle rrrrdd__dduummpp__oopptt__ccbb__ffiilleeoouutt should write to. There's no specific condition for the callback to detect when it is called for the first time, nor for the last time. If you require this for initialization and cleanup you should do those tasks before and after calling rrrrdd__dduummpp__ccrr__rr respectively. UUTTIILLIITTYY FFUUNNCCTTIIOONNSS _rr_rr_dd____rr_aa_nn_dd_oo_mm_((_)) Generates random numbers just like _r_a_n_d_o_m_(_). This further ensures that the random number generator is seeded exactly once per process. rrrrdd__aadddd__ppttrr((vvooiidd ******ddeesstt,, ssiizzee__tt **ddeesstt__ssiizzee,, vvooiidd **ssrrcc)) Dynamically resize the array pointed to by "dest". "dest_size" is a pointer to the current size of "dest". Upon successful _r_e_a_l_l_o_c_(_), the "dest_size" is incremented by 1 and the "src" pointer is stored at the end of the new "dest". Returns 1 on success, 0 on failure. type **arr = NULL; type *elem = "whatever"; size_t arr_size = 0; if (!rrd_add_ptr(&arr, &arr_size, elem)) handle_failure(); rrrrdd__aadddd__ssttrrdduupp((cchhaarr ******ddeesstt,, ssiizzee__tt **ddeesstt__ssiizzee,, cchhaarr **ssrrcc)) Like "rrd_add_ptr", except adds a "strdup" of the source string. char **arr = NULL; size_t arr_size = NULL; char *str = "example text"; if (!rrd_add_strdup(&arr, &arr_size, str)) handle_failure(); rrrrdd__ffrreeee__ppttrrss((vvooiidd ******ssrrcc,, ssiizzee__tt **ccnntt)) Free an array of pointers allocated by "rrd_add_ptr" or "rrd_add_strdup". Also frees the array pointer itself. On return, the source pointer will be NULL and the count will be zero. /* created as above */ rrd_free_ptrs(&arr, &arr_size); /* here, arr == NULL && arr_size == 0 */ rrrrdd__mmkkddiirr__pp((ccoonnsstt cchhaarr **ppaatthhnnaammee,, mmooddee__tt mmooddee)) Create the directory named "pathname" including all of its parent directories (similar to "mkdir -p" on the command line - see _m_k_d_i_r(1) for more information). The argument "mode" specifies the permissions to use. It is modified by the process's "umask". See _m_k_d_i_r(2) for more details. The function returns 0 on success, a negative value else. In case of an error, "errno" is set accordingly. Aside from the errors documented in _m_k_d_i_r(2), the function may fail with the following errors: EEIINNVVAALL "pathname" is "NULL" or the empty string. EENNOOMMEEMM Insufficient memory was available. aannyy eerrrroorr rreettuurrnneedd bbyy _ss_tt_aa_tt((22)) In contrast to _m_k_d_i_r(2), the function does nnoott fail if "pathname" already exists and is a directory. AAUUTTHHOORR RRD Contributors 1.4.7 2009-11-15 librrd(3) rrdtool-1.4.7/doc/rrdtutorial.txt0000644000175300017510000016350311707501552014031 00000000000000RRDTUTORIAL(1) rrdtool RRDTUTORIAL(1) NNAAMMEE rrdtutorial - Alex van den Bogaerdt's RRDtool tutorial DDEESSCCRRIIPPTTIIOONN RRDtool is written by Tobias Oetiker with contributions from many people all around the world. This document is written by Alex van den Bogaerdt to help you understand what RRDtool is and what it can do for you. The documentation provided with RRDtool can be too technical for some people. This tutorial is here to help you understand the basics of RRDtool. It should prepare you to read the documentation yourself. It also explains the general things about statistics with a focus on networking. TTUUTTOORRIIAALL IImmppoorrttaanntt Please don't skip ahead in this document! The first part of this document explains the basics and may be boring. But if you don't understand the basics, the examples will not be as meaningful to you. Sometimes things change. This example used to provide numbers like "0.04" in stead of "4.00000e-02". Those are really the same numbers, just written down differently. Don't be alarmed if a future version of rrdtool displays a slightly different form of output. The examples in this document are correct for version 1.2.0 of RRDtool. Also, sometimes bugs do occur. They may also influence the outcome of the examples. Example speed4.png was suffering from this (the handling of unknown data in an if-statement was wrong). Normal data will be just fine (a bug in rrdtool wouldn't last long) but special cases like NaN, INF and so on may last a bit longer. Try another version if you can, or just live with it. I fixed the speed4.png example (and added a note). There may be other examples which suffer from the same or a similar bug. Try to fix it yourself, which is a great excercise. But please do not submit your result as a fix to the source of this document. Discuss it on the user's list, or write to me. WWhhaatt iiss RRRRDDttooooll?? RRDtool refers to Round Robin Database tool. Round robin is a technique that works with a fixed amount of data, and a pointer to the current element. Think of a circle with some dots plotted on the edge. These dots are the places where data can be stored. Draw an arrow from the center of the circle to one of the dots; this is the pointer. When the current data is read or written, the pointer moves to the next element. As we are on a circle there is neither a beginning nor an end, you can go on and on and on. After a while, all the available places will be used and the process automatically reuses old locations. This way, the dataset will not grow in size and therefore requires no maintenance. RRDtool works with Round Robin Databases (RRDs). It stores and retrieves data from them. WWhhaatt ddaattaa ccaann bbee ppuutt iinnttoo aann RRRRDD?? You name it, it will probably fit as long as it is some sort of time- series data. This means you have to be able to measure some value at several points in time and provide this information to RRDtool. If you can do this, RRDtool will be able to store it. The values must be numerical but don't have to be integers, as is the case with MRTG (the next section will give more details on this more specialized application). Many examples below talk about SNMP which is an acronym for Simple Network Management Protocol. "Simple" refers to the protocol. It does not mean it is simple to manage or monitor a network. After working your way through this document, you should know enough to be able to understand what people are talking about. For now, just realize that SNMP can be used to query devices for the values of counters they keep. It is the value from those counters that we want to store in the RRD. WWhhaatt ccaann II ddoo wwiitthh tthhiiss ttooooll?? RRDtool originated from MRTG (Multi Router Traffic Grapher). MRTG started as a tiny little script for graphing the use of a university's connection to the Internet. MRTG was later (ab-)used as a tool for graphing other data sources including temperature, speed, voltage, number of printouts and the like. Most likely you will start to use RRDtool to store and process data collected via SNMP. The data will most likely be bytes (or bits) transferred from and to a network or a computer. But it can also be used to display tidal waves, solar radiation, power consumption, number of visitors at an exhibition, noise levels near an airport, temperature on your favorite holiday location, temperature in the fridge and whatever your imagination can come up with. You only need a sensor to measure the data and be able to feed the numbers into RRDtool. RRDtool then lets you create a database, store data in it, retrieve that data and create graphs in PNG format for display on a web browser. Those PNG images are dependent on the data you collected and could be, for instance, an overview of the average network usage, or the peaks that occurred. WWhhaatt iiff II ssttiillll hhaavvee pprroobblleemmss aafftteerr rreeaaddiinngg tthhiiss ddooccuummeenntt?? First of all: read it again! You may have missed something. If you are unable to compile the sources and you have a fairly common OS, it will probably not be the fault of RRDtool. There may be pre-compiled versions around on the Internet. If they come from trusted sources, get one of those. If on the other hand the program works but does not give you the expected results, it will be a problem with configuring it. Review your configuration and compare it with the examples that follow. There is a mailing list and an archive of it. Read the list for a few weeks and search the archive. It is considered rude to just ask a question without searching the archives: your problem may already have been solved for somebody else! This is true for most, if not all, mailing lists and not only for this particular one. Look in the documentation that came with RRDtool for the location and usage of the list. I suggest you take a moment to subscribe to the mailing list right now by sending an email to with a subject of "subscribe". If you ever want to leave this list, just write an email to the same address but now with a subject of "unsubscribe". HHooww wwiillll yyoouu hheellpp mmee?? By giving you some detailed descriptions with detailed examples. I assume that following the instructions in the order presented will give you enough knowledge of RRDtool to experiment for yourself. If it doesn't work the first time, don't give up. Reread the stuff that you did understand, you may have missed something. By following the examples you get some hands-on experience and, even more important, some background information of how it works. You will need to know something about hexadecimal numbers. If you don't then start with reading bin_dec_hex before you continue here. YYoouurr ffiirrsstt RRoouunndd RRoobbiinn DDaattaabbaassee In my opinion the best way to learn something is to actually do it. Why not start right now? We will create a database, put some values in it and extract this data again. Your output should be the same as the output that is included in this document. We will start with some easy stuff and compare a car with a router, or compare kilometers (miles if you wish) with bits and bytes. It's all the same: some number over some time. Assume we have a device that transfers bytes to and from the Internet. This device keeps a counter that starts at zero when it is turned on, increasing with every byte that is transferred. This counter will probably have a maximum value. If this value is reached and an extra byte is counted, the counter starts over at zero. This is the same as many counters in the world such as the mileage counter in a car. Most discussions about networking talk about bits per second so lets get used to that right away. Assume a byte is eight bits and start to think in bits not bytes. The counter, however, still counts bytes! In the SNMP world most of the counters are 32 bits. That means they are counting from 0 to 4294967295. We will use these values in the examples. The device, when asked, returns the current value of the counter. We know the time that has passes since we last asked so we now know how many bytes have been transferred ***on average*** per second. This is not very hard to calculate. First in words, then in calculations: 1. Take the current counter, subtract the previous value from it. 2. Do the same with the current time and the previous time (in seconds). 3. Divide the outcome of (1) by the outcome of (2), the result is the amount of bytes per second. Multiply by eight to get the number of bits per second (bps). bps = (counter_now - counter_before) / (time_now - time_before) * 8 For some people it may help to translate this to an automobile example. Do not try this example, and if you do, don't blame me for the results! People who are not used to think in kilometers per hour can translate most into miles per hour by dividing km by 1.6 (close enough). I will use the following abbreviations: m: meter km: kilometer (= 1000 meters). h: hour s: second km/h: kilometers per hour m/s: meters per second You are driving a car. At 12:05 you read the counter in the dashboard and it tells you that the car has moved 12345 km until that moment. At 12:10 you look again, it reads 12357 km. This means you have traveled 12 km in five minutes. A scientist would translate that into meters per second and this makes a nice comparison toward the problem of (bytes per five minutes) versus (bits per second). We traveled 12 kilometers which is 12000 meters. We did that in five minutes or 300 seconds. Our speed is 12000m / 300s or 40 m/s. We could also calculate the speed in km/h: 12 times 5 minutes is an hour, so we have to multiply 12 km by 12 to get 144 km/h. For our native English speaking friends: that's 90 mph so don't try this example at home or where I live :) Remember: these numbers are averages only. There is no way to figure out from the numbers, if you drove at a constant speed. There is an example later on in this tutorial that explains this. I hope you understand that there is no difference in calculating m/s or bps; only the way we collect the data is different. Even the k from kilo is the same as in networking terms k also means 1000. We will now create a database where we can keep all these interesting numbers. The method used to start the program may differ slightly from OS to OS, but I assume you can figure it out if it works different on your's. Make sure you do not overwrite any file on your system when executing the following command and type the whole line as one long line (I had to split it for readability) and skip all of the '\' characters. rrdtool create test.rrd \ --start 920804400 \ DS:speed:COUNTER:600:U:U \ RRA:AVERAGE:0.5:1:24 \ RRA:AVERAGE:0.5:6:10 (So enter: "rrdtool create test.rrd --start 920804400 DS ...") WWhhaatt hhaass bbeeeenn ccrreeaatteedd?? We created the round robin database called test (test.rrd) which starts at noon the day I started writing this document, 7th of March, 1999 (this date translates to 920804400 seconds as explained below). Our database holds one data source (DS) named "speed" that represents a counter. This counter is read every five minutes (this is the default therefore you don't have to put "--step=300"). In the same database two round robin archives (RRAs) are kept, one averages the data every time it is read (e.g., there's nothing to average) and keeps 24 samples (24 times 5 minutes is 2 hours). The other averages 6 values (half hour) and contains 10 such averages (e.g. 5 hours). RRDtool works with special time stamps coming from the UNIX world. This time stamp is the number of seconds that passed since January 1st 1970 UTC. The time stamp value is translated into local time and it will therefore look different for different time zones. Chances are that you are not in the same part of the world as I am. This means your time zone is different. In all examples where I talk about time, the hours may be wrong for you. This has little effect on the results of the examples, just correct the hours while reading. As an example: where I will see "12:05" the UK folks will see "11:05". We now have to fill our database with some numbers. We'll pretend to have read the following numbers: 12:05 12345 km 12:10 12357 km 12:15 12363 km 12:20 12363 km 12:25 12363 km 12:30 12373 km 12:35 12383 km 12:40 12393 km 12:45 12399 km 12:50 12405 km 12:55 12411 km 13:00 12415 km 13:05 12420 km 13:10 12422 km 13:15 12423 km We fill the database as follows: rrdtool update test.rrd 920804700:12345 920805000:12357 920805300:12363 rrdtool update test.rrd 920805600:12363 920805900:12363 920806200:12373 rrdtool update test.rrd 920806500:12383 920806800:12393 920807100:12399 rrdtool update test.rrd 920807400:12405 920807700:12411 920808000:12415 rrdtool update test.rrd 920808300:12420 920808600:12422 920808900:12423 This reads: update our test database with the following numbers time 920804700, value 12345 time 920805000, value 12357 etcetera. As you can see, it is possible to feed more than one value into the database in one command. I had to stop at three for readability but the real maximum per line is OS dependent. We can now retrieve the data from our database using "rrdtool fetch": rrdtool fetch test.rrd AVERAGE --start 920804400 --end 920809200 It should return the following output: speed 920804700: nan 920805000: 4.0000000000e-02 920805300: 2.0000000000e-02 920805600: 0.0000000000e+00 920805900: 0.0000000000e+00 920806200: 3.3333333333e-02 920806500: 3.3333333333e-02 920806800: 3.3333333333e-02 920807100: 2.0000000000e-02 920807400: 2.0000000000e-02 920807700: 2.0000000000e-02 920808000: 1.3333333333e-02 920808300: 1.6666666667e-02 920808600: 6.6666666667e-03 920808900: 3.3333333333e-03 920809200: nan 920809500: nan Note that you might get more rows than you expect. The reason for this is that you ask for a time range that ends on 920809200. The number that is written behind 920809200: in the list above covers the time range from 920808900 to 920809200, EXCLUDING 920809200. Hence to be on the sure side, you receive the entry from 920809200 to 920809500 as well since it INCLUDES 920809200. You may also see "NaN" instead of "nan" this is OS dependent. "NaN" stands for "Not A Number". If your OS writes "U" or "UNKN" or something similar that's okay. If something else is wrong, it will probably be due to an error you made (assuming that my tutorial is correct of course :-). In that case: delete the database and try again. The meaning of the above output will become clear below. TTiimmee ttoo ccrreeaattee ssoommee ggrraapphhiiccss Try the following command: rrdtool graph speed.png \ --start 920804400 --end 920808000 \ DEF:myspeed=test.rrd:speed:AVERAGE \ LINE2:myspeed#FF0000 This will create speed.png which starts at 12:00 and ends at 13:00. There is a definition of a variable called myspeed, using the data from RRA "speed" out of database "test.rrd". The line drawn is 2 pixels high and represents the variable myspeed. The color is red (specified by its rgb-representation, see below). You'll notice that the start of the graph is not at 12:00 but at 12:05. This is because we have insufficient data to tell the average before that time. This will only happen when you miss some samples, this will not happen a lot, hopefully. If this has worked: congratulations! If not, check what went wrong. The colors are built up from red, green and blue. For each of the components, you specify how much to use in hexadecimal where 00 means not included and FF means fully included. The "color" white is a mixture of red, green and blue: FFFFFF The "color" black is all colors off: 000000 red #FF0000 green #00FF00 blue #0000FF magenta #FF00FF (mixed red with blue) gray #555555 (one third of all components) Additionally you can (with a recent RRDtool) add an alpha channel (transparency). The default will be "FF" which means non-transparent. The PNG you just created can be displayed using your favorite image viewer. Web browsers will display the PNG via the URL "file:///the/path/to/speed.png" GGrraapphhiiccss wwiitthh ssoommee mmaatthh When looking at the image, you notice that the horizontal axis is labeled 12:10, 12:20, 12:30, 12:40 and 12:50. Sometimes a label doesn't fit (12:00 and 13:00 would be likely candidates) so they are skipped. The vertical axis displays the range we entered. We provided kilometers and when divided by 300 seconds, we get very small numbers. To be exact, the first value was 12 (12357-12345) and divided by 300 this makes 0.04, which is displayed by RRDtool as "40 m" meaning "40/1000". The "m" (milli) has nothing to do with meters (also m), kilometers or millimeters! RRDtool doesn't know about the physical units of our data, it just works with dimensionless numbers. If we had measured our distances in meters, this would have been (12357000-12345000)/300 = 12000/300 = 40. As most people have a better feel for numbers in this range, we'll correct that. We could recreate our database and store the correct data, but there is a better way: we do some calculations while creating the png file! rrdtool graph speed2.png \ --start 920804400 --end 920808000 \ --vertical-label m/s \ DEF:myspeed=test.rrd:speed:AVERAGE \ CDEF:realspeed=myspeed,1000,\* \ LINE2:realspeed#FF0000 Note: I need to escape the multiplication operator * with a backslash. If I don't, the operating system may interpret it and use it for file name expansion. You could also place the line within quotation marks like so: "CDEF:realspeed=myspeed,1000,*" \ It boils down to: it is RRDtool which should see *, not your shell. And it is your shell interpreting \, not RRDtool. You may need to adjust examples accordingly if you happen to use an operating system or shell which behaves differently. After viewing this PNG, you notice the "m" (milli) has disappeared. This it what the correct result would be. Also, a label has been added to the image. Apart from the things mentioned above, the PNG should look the same. The calculations are specified in the CDEF part above and are in Reverse Polish Notation ("RPN"). What we requested RRDtool to do is: "take the data source myspeed and the number 1000; multiply those". Don't bother with RPN yet, it will be explained later on in more detail. Also, you may want to read my tutorial on CDEFs and Steve Rader's tutorial on RPN. But first finish this tutorial. Hang on! If we can multiply values with 1000, it should also be possible to display kilometers per hour from the same data! To change a value that is measured in meters per second: Calculate meters per hour: value * 3600 Calculate kilometers per hour: value / 1000 Together this makes: value * (3600/1000) or value * 3.6 In our example database we made a mistake and we need to compensate for this by multiplying with 1000. Applying that correction: value * 3.6 * 1000 == value * 3600 Now let's create this PNG, and add some more magic ... rrdtool graph speed3.png \ --start 920804400 --end 920808000 \ --vertical-label km/h \ DEF:myspeed=test.rrd:speed:AVERAGE \ "CDEF:kmh=myspeed,3600,*" \ CDEF:fast=kmh,100,GT,kmh,0,IF \ CDEF:good=kmh,100,GT,0,kmh,IF \ HRULE:100#0000FF:"Maximum allowed" \ AREA:good#00FF00:"Good speed" \ AREA:fast#FF0000:"Too fast" Note: here we use another means to escape the * operator by enclosing the whole string in double quotes. This graph looks much better. Speed is shown in km/h and there is even an extra line with the maximum allowed speed (on the road I travel on). I also changed the colors used to display speed and changed it from a line into an area. The calculations are more complex now. For speed measurements within the speed limit they are: Check if kmh is greater than 100 ( kmh,100 ) GT If so, return 0, else kmh ((( kmh,100 ) GT ), 0, kmh) IF For values above the speed limit: Check if kmh is greater than 100 ( kmh,100 ) GT If so, return kmh, else return 0 ((( kmh,100) GT ), kmh, 0) IF GGrraapphhiiccss MMaaggiicc I like to believe there are virtually no limits to how RRDtool graph can manipulate data. I will not explain how it works, but look at the following PNG: rrdtool graph speed4.png \ --start 920804400 --end 920808000 \ --vertical-label km/h \ DEF:myspeed=test.rrd:speed:AVERAGE \ CDEF:nonans=myspeed,UN,0,myspeed,IF \ CDEF:kmh=nonans,3600,* \ CDEF:fast=kmh,100,GT,100,0,IF \ CDEF:over=kmh,100,GT,kmh,100,-,0,IF \ CDEF:good=kmh,100,GT,0,kmh,IF \ HRULE:100#0000FF:"Maximum allowed" \ AREA:good#00FF00:"Good speed" \ AREA:fast#550000:"Too fast" \ STACK:over#FF0000:"Over speed" Remember the note in the beginning? I had to remove unknown data from this example. The 'nonans' CDEF is new, and the 6th line (which used to be the 5th line) used to read 'CDEF:kmh=myspeed,3600,*' Let's create a quick and dirty HTML page to view the three PNGs: Speed Speed in meters per second
Speed in kilometers per hour
Traveled too fast? Name the file "speed.html" or similar, and look at it in your web browser. Now, all you have to do is measure the values regularly and update the database. When you want to view the data, recreate the PNGs and make sure to refresh them in your browser. (Note: just clicking reload may not be enough, especially when proxies are involved. Try shift-reload or ctrl-F5). UUppddaatteess iinn RReeaalliittyy We've already used the "update" command: it took one or more parameters in the form of "
/ []: : If the --ll option is not specified the default address, "unix:/tmp/rrdcached.sock", will be used. --ss _g_r_o_u_p___n_a_m_e|_g_i_d Set the group permissions of a UNIX domain socket. The option accepts either a numeric group id or group name. That group will then have both read and write permissions (the socket will have file permissions 0750) for the socket and, therefore, is able to send commands to the daemon. This may be useful in cases where you cannot easily run all RRD processes with the same user privileges (e.g. graph generating CGI scripts that typically run in the permission context of the web server). This option affects the _f_o_l_l_o_w_i_n_g UNIX socket addresses (the following --ll options) or the default socket (if no --ll options have been specified), i.e., you may specify different settings for different sockets. The default is not to change ownership or permissions of the socket and, thus, use the system default. --mm _m_o_d_e Set the file permissions of a UNIX domain socket. The option accepts an octal number representing the bit pattern for the mode (see _c_h_m_o_d(1) for details). Please note that not all systems honor this setting. On Linux, read/write permissions are required to connect to a UNIX socket. However, many BSD-derived systems ignore permissions for UNIX sockets. See _u_n_i_x(7) for details. This option affects the _f_o_l_l_o_w_i_n_g UNIX socket addresses (the following --ll options) or the default socket (if no --ll options have been specified), i.e., you may specify different settings for different sockets. The default is not to change ownership or permissions of the socket and, thus, use the system default. --PP _c_o_m_m_a_n_d[,_c_o_m_m_a_n_d[,...]] Specifies the commands accepted via a network socket. This allows administrators of _R_R_D_C_a_c_h_e_D to control the actions accepted from various sources. The arguments given to the --PP option is a comma separated list of commands. For example, to allow the "FLUSH" and "PENDING" commands one could specify: rrdcached -P FLUSH,PENDING $MORE_ARGUMENTS The --PP option affects the _f_o_l_l_o_w_i_n_g socket addresses (the following --ll options) or the default socket (if no --ll options have been specified). In the following example, only the IPv4 network socket (address 10.0.0.1) will be restricted to the "FLUSH" and "PENDING" commands: rrdcached -l unix:/some/path -P FLUSH,PENDING -l 10.0.0.1 A complete list of available commands can be found in the section "Valid Commands" below. There are two minor special exceptions: · The "HELP" and "QUIT" commands are always allowed. · If the "BATCH" command is accepted, the .. command will automatically be accepted, too. Please also read "SECURITY CONSIDERATIONS" below. --ww _t_i_m_e_o_u_t Data is written to disk every _t_i_m_e_o_u_t seconds. If this option is not specified the default interval of 300 seconds will be used. --zz _d_e_l_a_y If specified, rrdcached will delay writing of each RRD for a random number of seconds in the range [0,_d_e_l_a_y). This will avoid too many writes being queued simultaneously. This value should be no greater than the value specified in --ww. By default, there is no delay. --ff _t_i_m_e_o_u_t Every _t_i_m_e_o_u_t seconds the entire cache is searched for old values which are written to disk. This only concerns files to which updates have stopped, so setting this to a high value, such as 3600 seconds, is acceptable in most cases. This timeout defaults to 3600 seconds. --pp _f_i_l_e Sets the name and location of the PID-file. If not specified, the default, "_$_l_o_c_a_l_s_t_s_t_e_d_i_r_/_r_u_n_/_r_r_d_c_a_c_h_e_d_._p_i_d_" will be used. --tt _w_r_i_t_e___t_h_r_e_a_d_s Specifies the number of threads used for writing RRD files. The default is 4. Increasing this number will allow rrdcached to have more simultaneous I/O requests into the kernel. This may allow the kernel to re-order disk writes, resulting in better disk throughput. --jj _d_i_r Write updates to a journal in _d_i_r. In the event of a program or system crash, this will allow the daemon to write any updates that were pending at the time of the crash. On startup, the daemon will check for journal files in this directory. If found, all updates therein will be read into memory before the daemon starts accepting new connections. The journal will be rotated with the same frequency as the flush timer given by --ff. When journaling is enabled, the daemon will use a fast shutdown procedure. Rather than flushing all files to disk, it will make sure the journal is properly written and exit immediately. Although the RRD data files are not fully up-to-date, no information is lost; all pending updates will be replayed from the journal next time the daemon starts up. To disable fast shutdown, use the --FF option. --FF ALWAYS flush all updates to the RRD data files when the daemon is shut down, regardless of journal setting. --gg Run in the foreground. The daemon will not _f_o_r_k_(_). --bb _d_i_r The daemon will change into a specific directory at startup. All files passed to the daemon, that are specified by a rreellaattiivvee path, will be interpreted to be relative to this directory. If not given the default, "/tmp", will be used. +------------------------+------------------------+ ! Command line ! File updated ! +------------------------+------------------------+ ! foo.rrd ! /tmp/foo.rrd ! ! foo/bar.rrd ! /tmp/foo/bar.rrd ! ! /var/lib/rrd/foo.rrd ! /var/lib/rrd/foo.rrd ! +------------------------+------------------------+ Paths given on the command line and paths actually updated by the daemon, assuming the base directory "/tmp". WWAARRNNIINNGG:: The paths up to and including the base directory MMUUSSTT NNOOTT BBEE symbolic links. In other words, if the base directory is specified as: -b /base/dir/somewhere ... then NNOONNEE of the following should be symbolic links: /base /base/dir /base/dir/somewhere --BB Only permit writes into the base directory specified in --bb (and any sub-directories). This does NNOOTT detect symbolic links. Paths containing "../" will also be blocked. AAFFFFEECCTTEEDD RRRRDDTTOOOOLL CCOOMMMMAANNDDSS The following commands may be made aware of the rrrrddccaacchheedd using the command line argument ----ddaaeemmoonn or the environment variable RRRRDDCCAACCHHEEDD__AADDDDRREESSSS: · dump · fetch · flush · graph · graphv · info · last · lastupdate · update · xport The uuppddaattee command can send values to the daemon instead of writing them to the disk itself. All other commands can send a FFLLUUSSHH command (see below) to the daemon before accessing the files, so they work with up-to-date data even if the cache timeout is large. EERRRROORR RREEPPOORRTTIINNGG The daemon reports errors in one of two ways: During startup, error messages are printed to "STDERR". One of the steps when starting up is to fork to the background and closing "STDERR" - after this writing directly to the user is no longer possible. Once this has happened, the daemon will send log messages to the system logging daemon using _s_y_s_l_o_g(3). The facility used is "LOG_DAEMON". HHOOWW IITT WWOORRKKSS When receiving an update, rrrrddccaacchheedd does not write to disk but looks for an entry for that file in its internal tree. If not found, an entry is created including the current time (called "First" in the diagram below). This time is nnoott the time specified on the command line but the time the operating system considers to be "now". The value and time of the value (called "Time" in the diagram below) are appended to the tree node. When appending a value to a tree node, it is checked whether it's time to write the values to disk. Values are written to disk if "now() - First >= timeout", where "timeout" is the timeout specified using the --ww option, see "OPTIONS". If the values are "old enough" they will be enqueued in the "update queue", i. e. they will be appended to the linked list shown below. Because the tree nodes and the elements of the linked list are the same data structures in memory, any update to a file that has already been enqueued will be written with the next write to the RRD file, too. A separate "update thread" constantly dequeues the first element in the update queue and writes all its values to the appropriate file. So as long as the update queue is not empty files are written at the highest possible rate. Since the timeout of files is checked only when new values are added to the file, "dead" files, i. e. files that are not updated anymore, would never be written to disk. Therefore, every now and then, controlled by the --ff option, the entire tree is walked and all "old" values are enqueued. Since this only affects "dead" files and walking the tree is relatively expensive, you should set the "flush interval" to a reasonably high value. The default is 3600 seconds (one hour). The downside of caching values is that they won't show up in graphs generated from the RRD files. To get around this, the daemon provides the "flush command" to flush specific files. This means that the file is inserted at the hheeaadd of the update queue or moved there if it is already enqueued. The flush command will return only after the file's pending updates have been written to disk. +------+ +------+ +------+ ! head ! ! root ! ! tail ! +---+--+ +---+--+ +---+--+ ! /\ ! ! / \ ! ! /\ /\ ! ! /\/\ \ `----------------- ... --------, ! V / `-------, ! V +---+----+---+ +------+-----+ +---+----+---+ ! File: foo ! ! File: bar ! ! File: qux ! ! First: 101 ! ! First: 119 ! ! First: 180 ! ! Next:&bar -+--->! Next:&... -+---> ... --->! Next:NULL ! | Prev:NULL !<---+-Prev:&foo !<--- ... ----+-Prev: &... ! +============+ +============+ +============+ ! Time: 100 ! ! Time: 120 ! ! Time: 180 ! ! Value: 10 ! ! Value: 0.1 ! ! Value: 2,2 ! +------------+ +------------+ +------------+ ! Time: 110 ! ! Time: 130 ! ! Time: 190 ! ! Value: 26 ! ! Value: 0.1 ! ! Value: 7,3 ! +------------+ +------------+ +------------+ : : : : : : +------------+ +------------+ +------------+ ! Time: 230 ! ! Time: 250 ! ! Time: 310 ! ! Value: 42 ! ! Value: 0.2 ! ! Value: 1,2 ! +------------+ +------------+ +------------+ The above diagram demonstrates: · Files/values are stored in a (balanced) tree. · Tree nodes and entries in the update queue are the same data structure. · The local time ("First") and the time specified in updates ("Time") may differ. · Timed out values are inserted at the "tail". · Explicitly flushed values are inserted at the "head". · ASCII art rocks. SSEECCUURRIITTYY CCOONNSSIIDDEERRAATTIIOONNSS AAuutthheennttiiccaattiioonn If your rrdtool installation was built without libwrap there is no form of authentication for clients connecting to the rrdcache daemon! If your rrdtool installation was built with libwrap then you can use hosts_access to restrict client access to the rrdcache daemon (rrdcached). For more information on how to use hosts_access to restrict access to the rrdcache daemon you should read the _h_o_s_t_s___a_c_c_e_s_s(5) man pages. It is still highly recommended to install a packet filter or similar mechanism to prevent unauthorized connections. Unless you have a dedicated VLAN or VPN for this, using network sockets is probably a bad idea! AAuutthhoorriizzaattiioonn There is minimal per-socket authorization. Authorization is currently done on a per-socket basis. That means each socket has a list of commands it will accept and it will accept. It will accept only those commands explicitly listed but it will (currently) accept these commands from anyone reaching the socket. If the networking sockets are to be used, it is necessary to restrict the accepted commands to those needed by external clients. If, for example, external clients want to draw graphs of the cached data, they should only be allowed to use the "FLUSH" command. EEnnccrryyppttiioonn There is no encryption. Again, this may be added in the future, but for the time being it is your job to keep your private data private. Install a VPN or an encrypted tunnel if you statistics are confidential! SSaanniittyy cchheecckkiinngg There is no sanity checking. The daemon will blindly write to any file it gets told, so you really should create a separate user just for this daemon. Also it does not do any sanity checks, so if it gets told to write values for a time far in the future, your files will be messed up good! CCoonncclluussiioonn · Security is the job of the administrator. · We recommend to allow write access via UNIX domain sockets only. · You have been warned. PPRROOTTOOCCOOLL The daemon communicates with clients using a line based ASCII protocol which is easy to read and easy to type. This makes it easy for scripts to implement the protocol and possible for users to use telnet to connect to the daemon and test stuff "by hand". The protocol is line based, this means that each record consists of one or more lines. A line is terminated by the line feed character 0x0A, commonly written as "\n". In the examples below, this character will be written as "" ("line feed"). After the connection has been established, the client is expected to send a "command". A command consists of the command keyword, possibly some arguments, and a terminating newline character. For a list of commands, see "Valid Commands" below. Example: FLUSH /tmp/foo.rrd The daemon answers with a line consisting of a status code and a short status message, separated by one or more space characters. A negative status code signals an error, a positive status code or zero signal success. If the status code is greater than zero, it indicates the number of lines that follow the status line. Examples: 0 Success 2 Two lines follow This is the first line And this is the second line VVaalliidd CCoommmmaannddss The following commands are understood by the daemon: FFLLUUSSHH _f_i_l_e_n_a_m_e Causes the daemon to put _f_i_l_e_n_a_m_e to the hheeaadd of the update queue (possibly moving it there if the node is already enqueued). The answer will be sent aafftteerr the node has been dequeued. FFLLUUSSHHAALLLL Causes the daemon to start flushing ALL pending values to disk. This returns immediately, even though the writes may take a long time. PPEENNDDIINNGG _f_i_l_e_n_a_m_e Shows any "pending" updates for a file, in order. The updates shown have not yet been written to the underlying RRD file. FFOORRGGEETT _f_i_l_e_n_a_m_e Removes _f_i_l_e_n_a_m_e from the cache. Any pending updates WWIILLLL BBEE LLOOSSTT. QQUUEEUUEE Shows the files that are on the output queue. Returns zero or more lines in the following format, where is the number of values to be written for the : HHEELLPP [_c_o_m_m_a_n_d] Returns a short usage message. If no command is given, or _c_o_m_m_a_n_d is HHEELLPP, a list of commands supported by the daemon is returned. Otherwise a short description, possibly containing a pointer to a manual page, is returned. Obviously, this is meant for interactive usage and the format in which the commands and usage summaries are returned is not well defined. SSTTAATTSS Returns a list of metrics which can be used to measure the daemons performance and check its status. For a description of the values returned, see "Performance Values" below. The format in which the values are returned is similar to many other line based protocols: Each value is printed on a separate line, each consisting of the name of the value, a colon, one or more spaces and the actual value. Example: 9 Statistics follow QueueLength: 0 UpdatesReceived: 30 FlushesReceived: 2 UpdatesWritten: 13 DataSetsWritten: 390 TreeNodesNumber: 13 TreeDepth: 4 JournalBytes: 190 JournalRotate: 0 UUPPDDAATTEE _f_i_l_e_n_a_m_e _v_a_l_u_e_s [_v_a_l_u_e_s ...] Adds more data to a filename. This is tthhee operation the daemon was designed for, so describing the mechanism again is unnecessary. Read "HOW IT WORKS" above for a detailed explanation. Note that rrdcached only accepts absolute timestamps in the update values. Updates strings like "N:1:2:3" are automatically converted to absolute time by the RRD client library before sending to rrdcached. WWRROOTTEE _f_i_l_e_n_a_m_e This command is written to the journal after a file is successfully written out to disk. It is used during journal replay to determine which updates have already been applied. It is _o_n_l_y valid in the journal; it is not accepted from the other command channels. BBAATTCCHH This command initiates the bulk load of multiple commands. This is designed for installations with extremely high update rates, since it permits more than one command to be issued per _r_e_a_d_(_) and _w_r_i_t_e_(_). All commands are executed just as they would be if given individually, except for output to the user. Messages indicating success are suppressed, and error messages are delayed until the client is finished. Command processing is finished when the client sends a dot (".") on its own line. After the client has finished, the server responds with an error count and the list of error messages (if any). Each error messages indicates the number of the command to which it corresponds, and the error message itself. The first user command after BBAATTCCHH is command number one. client: BATCH server: 0 Go ahead. End with dot '.' on its own line. client: UPDATE x.rrd 1223661439:1:2:3 <--- command #1 client: UPDATE y.rrd 1223661440:3:4:5 <--- command #2 client: and so on... client: . server: 2 Errors server: 1 message for command 1 server: 12 message for command 12 QQUUIITT Disconnect from rrdcached. PPeerrffoorrmmaannccee VVaalluueess The following counters are returned by the SSTTAATTSS command: QQuueeuueeLLeennggtthh _(_u_n_s_i_g_n_e_d _6_4_b_i_t _i_n_t_e_g_e_r_) Number of nodes currently enqueued in the update queue. UUppddaatteessRReecceeiivveedd _(_u_n_s_i_g_n_e_d _6_4_b_i_t _i_n_t_e_g_e_r_) Number of UPDATE commands received. FFlluusshheessRReecceeiivveedd _(_u_n_s_i_g_n_e_d _6_4_b_i_t _i_n_t_e_g_e_r_) Number of FLUSH commands received. UUppddaatteessWWrriitttteenn _(_u_n_s_i_g_n_e_d _6_4_b_i_t _i_n_t_e_g_e_r_) Total number of updates, i. e. calls to "rrd_update_r", since the daemon was started. DDaattaaSSeettssWWrriitttteenn _(_u_n_s_i_g_n_e_d _6_4_b_i_t _i_n_t_e_g_e_r_) Total number of "data sets" written to disk since the daemon was started. A data set is one or more values passed to the UUPPDDAATTEE command. For example: "1223661439:123:456" is one data set with two values. The term "data set" is used to prevent confusion whether individual values or groups of values are counted. TTrreeeeNNooddeessNNuummbbeerr _(_u_n_s_i_g_n_e_d _6_4_b_i_t _i_n_t_e_g_e_r_) Number of nodes in the cache. TTrreeeeDDeepptthh _(_u_n_s_i_g_n_e_d _6_4_b_i_t _i_n_t_e_g_e_r_) Depth of the tree used for fast key lookup. JJoouurrnnaallBByytteess _(_u_n_s_i_g_n_e_d _6_4_b_i_t _i_n_t_e_g_e_r_) Total number of bytes written to the journal since startup. JJoouurrnnaallRRoottaattee _(_u_n_s_i_g_n_e_d _6_4_b_i_t _i_n_t_e_g_e_r_) Number of times the journal has been rotated since startup. SSIIGGNNAALLSS SIGINT and SIGTERM The daemon exits normally on receipt of either of these signals. Pending updates are handled in accordance with the --jj and --FF options. SIGUSR1 The daemon exits AFTER flushing all updates out to disk. This may take a while. SIGUSR2 The daemon exits immediately, without flushing updates out to disk. Pending updates will be replayed from the journal when the daemon starts up again. WWAARRNNIINNGG:: iiff jjoouurrnnaalliinngg ((--jj)) iiss NNOOTT eennaabblleedd,, aannyy ppeennddiinngg uuppddaatteess WWIILLLL BBEE LLOOSSTT. BBUUGGSS No known bugs at the moment. SSEEEE AALLSSOO rrdtool, rrdgraph AAUUTTHHOORR Florian Forster Both rrrrddccaacchheedd and this manual page have been written by Florian. CCOONNTTRRIIBBUUTTOORRSS kevin brintnall 1.4.7 2011-03-15 RRDCACHED(1) rrdtool-1.4.7/doc/rrdtool.txt0000644000175300017510000003564011707501551013142 00000000000000RRDTOOL(1) rrdtool RRDTOOL(1) NNAAMMEE rrdtool - Round Robin Database Tool SSYYNNOOPPSSIISS rrrrddttooooll -- [workdir]| _f_u_n_c_t_i_o_n DDEESSCCRRIIPPTTIIOONN OOVVEERRVVIIEEWW It is pretty easy to gather status information from all sorts of things, ranging from the temperature in your office to the number of octets which have passed through the FDDI interface of your router. But it is not so trivial to store this data in an efficient and systematic manner. This is where RRRRDDttooooll comes in handy. It lets you _l_o_g _a_n_d _a_n_a_l_y_z_e the data you gather from all kinds of data-sources (DDSS). The data analysis part of RRDtool is based on the ability to quickly generate graphical representations of the data values collected over a definable time period. In this man page you will find general information on the design and functionality of the Round Robin Database Tool (RRDtool). For a more detailed description of how to use the individual functions of RRRRDDttooooll check the corresponding man page. For an introduction to the usage of RRDtool make sure you consult the rrdtutorial. FFUUNNCCTTIIOONNSS While the man pages talk of command line switches you have to set in order to make RRRRDDttooooll work it is important to note that RRRRDDttooooll can be remotely controlled through a set of pipes. This saves a considerable amount of startup time when you plan to make RRRRDDttooooll do a lot of things quickly. Check the section on Remote_Control further down. There is also a number of language bindings for RRDtool which allow you to use it directly from Perl, python, Tcl, PHP, etc. ccrreeaattee Set up a new Round Robin Database (RRD). Check rrdcreate. uuppddaattee Store new data values into an RRD. Check rrdupdate. uuppddaatteevv Operationally equivalent to uuppddaattee except for output. Check rrdupdate. ggrraapphh Create a graph from data stored in one or several RRDs. Apart from generating graphs, data can also be extracted to stdout. Check rrdgraph. dduummpp Dump the contents of an RRD in plain ASCII. In connection with restore you can use this to move an RRD from one computer architecture to another. Check rrddump. rreessttoorree Restore an RRD in XML format to a binary RRD. Check rrdrestore ffeettcchh Get data for a certain time period from a RRD. The graph function uses fetch to retrieve its data from an RRD. Check rrdfetch. ttuunnee Alter setup of an RRD. Check rrdtune. llaasstt Find the last update time of an RRD. Check rrdlast. iinnffoo Get information about an RRD. Check rrdinfo. rrrrddrreessiizzee Change the size of individual RRAs. This is dangerous! Check rrdresize. xxppoorrtt Export data retrieved from one or several RRDs. Check rrdxport. fflluusshhccaacchheedd Flush the values for a specific RRD file from memory. Check rrdflushcached. rrrrddccggii This is a standalone tool for producing RRD graphs on the fly. Check rrdcgi. HHOOWW DDOOEESS RRRRDDTTOOOOLL WWOORRKK?? Data Acquisition When monitoring the state of a system, it is convenient to have the data available at a constant time interval. Unfortunately, you may not always be able to fetch data at exactly the time you want to. Therefore RRRRDDttooooll lets you update the log file at any time you want. It will automatically interpolate the value of the data-source (DDSS) at the latest official time-slot (interval) and write this interpolated value to the log. The original value you have supplied is stored as well and is also taken into account when interpolating the next log entry. Consolidation You may log data at a 1 minute interval, but you might also be interested to know the development of the data over the last year. You could do this by simply storing the data in 1 minute intervals for the whole year. While this would take considerable disk space it would also take a lot of time to analyze the data when you wanted to create a graph covering the whole year. RRRRDDttooooll offers a solution to this problem through its data consolidation feature. When setting up an Round Robin Database (RRRRDD), you can define at which interval this consolidation should occur, and what consolidation function (CCFF) (average, minimum, maximum, total, last) should be used to build the consolidated values (see rrdcreate). You can define any number of different consolidation setups within one RRRRDD. They will all be maintained on the fly when new data is loaded into the RRRRDD. Round Robin Archives Data values of the same consolidation setup are stored into Round Robin Archives (RRRRAA). This is a very efficient manner to store data for a certain amount of time, while using a known and constant amount of storage space. It works like this: If you want to store 1'000 values in 5 minute interval, RRRRDDttooooll will allocate space for 1'000 data values and a header area. In the header it will store a pointer telling which slots (value) in the storage area was last written to. New values are written to the Round Robin Archive in, you guessed it, a round robin manner. This automatically limits the history to the last 1'000 values (in our example). Because you can define several RRRRAAs within a single RRRRDD, you can setup another one, for storing 750 data values at a 2 hour interval, for example, and thus keep a log for the last two months at a lower resolution. The use of RRRRAAs guarantees that the RRRRDD does not grow over time and that old data is automatically eliminated. By using the consolidation feature, you can still keep data for a very long time, while gradually reducing the resolution of the data along the time axis. Using different consolidation functions (CCFF) allows you to store exactly the type of information that actually interests you: the maximum one minute traffic on the LAN, the minimum temperature of your wine cellar, the total minutes of down time, etc. Unknown Data As mentioned earlier, the RRRRDD stores data at a constant interval. Sometimes it may happen that no new data is available when a value has to be written to the RRRRDD. Data acquisition may not be possible for one reason or other. With RRRRDDttooooll you can handle these situations by storing an _*_U_N_K_N_O_W_N_* value into the database. The value '_*_U_N_K_N_O_W_N_*' is supported through all the functions of the tool. When consolidating a data set, the amount of _*_U_N_K_N_O_W_N_* data values is accounted for and when a new consolidated value is ready to be written to its Round Robin Archive (RRRRAA), a validity check is performed to make sure that the percentage of unknown values in the data point is above a configurable level. If not, an _*_U_N_K_N_O_W_N_* value will be written to the RRRRAA. Graphing RRRRDDttooooll allows you to generate reports in numerical and graphical form based on the data stored in one or several RRRRDDs. The graphing feature is fully configurable. Size, color and contents of the graph can be defined freely. Check rrdgraph for more information on this. Aberrant Behavior Detection by Jake Brutlag RRRRDDttooooll provides the building blocks for near real-time aberrant behavior detection. These components include: · An algorithm for predicting the value of a time series one time step into the future. · A measure of deviation between predicted and observed values. · A mechanism to decide if and when an observed value or sequence of observed values is _t_o_o _d_e_v_i_a_n_t from the predicted value(s). Here is a brief explanation of these components: The Holt-Winters time series forecasting algorithm is an on- line (or incremental) algorithm that adaptively predicts future observations in a time series. Its forecast is the sum of three components: a baseline (or intercept), a linear trend over time (or slope), and a seasonal coefficient (a periodic effect, such as a daily cycle). There is one seasonal coefficient for each time point in the period (cycle). After a value is observed, each of these components is updated via exponential smoothing. This means that the algorithm "learns" from past values and uses them to predict the future. The rate of adaptation is governed by 3 parameters, alpha (intercept), beta (slope), and gamma (seasonal). The prediction can also be viewed as a smoothed value for the time series. The measure of deviation is a seasonal weighted absolute deviation. The term _s_e_a_s_o_n_a_l means deviation is measured separately for each time point in the seasonal cycle. As with Holt-Winters forecasting, deviation is predicted using the measure computed from past values (but only at that point in the seasonal cycle). After the value is observed, the algorithm learns from the observed value via exponential smoothing. Confidence bands for the observed time series are generated by scaling the sequence of predicted deviation values (we usually think of the sequence as a continuous line rather than a set of discrete points). Aberrant behavior (a potential failure) is reported whenever the number of times the observed value violates the confidence bands meets or exceeds a specified threshold within a specified temporal window (e.g. 5 violations during the past 45 minutes with a value observed every 5 minutes). This functionality is embedded in a set of related RRRRAAss. In particular, a FAILURES RRRRAA logs potential failures. With these data you could, for example, use a front-end application to RRRRDDttooooll to initiate real-time alerts. For a detailed description on how to set this up, see rrdcreate. RREEMMOOTTEE CCOONNTTRROOLL When you start RRRRDDttooooll with the command line option '--' it waits for input via standard input (STDIN). With this feature you can improve performance by attaching RRRRDDttooooll to another process (MRTG is one example) through a set of pipes. Over these pipes RRRRDDttooooll accepts the same arguments as on the command line and some special commands like qquuiitt,, ccdd,, mmkkddiirr and llss. For detailed help on the server commands type: rrdtool help cd|mkdir|pwd|ls|quit When a command is completed, RRDtool will print the string '"OK"', followed by timing information of the form uu::_u_s_e_r_t_i_m_e ss::_s_y_s_t_e_m_t_i_m_e. Both values are the running totals of seconds since RRDtool was started. If an error occurs, a line of the form '"ERROR:" _D_e_s_c_r_i_p_t_i_o_n _o_f _e_r_r_o_r' will be printed instead. RRRRDDttooooll will not abort, unless something really serious happens. If a wwoorrkkddiirr is specified and the UID is 0, RRDtool will do a chroot to that workdir. If the UID is not 0, RRDtool only changes the current directory to wwoorrkkddiirr. RRRRDD SSeerrvveerr If you want to create a RRD-Server, you must choose a TCP/IP Service number and add them to _/_e_t_c_/_s_e_r_v_i_c_e_s like this: rrdsrv 13900/tcp # RRD server Attention: the TCP port 13900 isn't officially registered for rrdsrv. You can use any unused port in your services file, but the server and the client system must use the same port, of course. With this configuration you can add RRDtool as meta-server to _/_e_t_c_/_i_n_e_t_d_._c_o_n_f. For example: rrdsrv stream tcp nowait root /opt/rrd/bin/rrdtool rrdtool - /var/rrd Don't forget to create the database directory /var/rrd and reinitialize your inetd. If all was setup correctly, you can access the server with Perl sockets, tools like netcat, or in a quick interactive test by using 'telnet localhost rrdsrv'. NNOOTTEE:: that there is no authentication with this feature! Do not setup such a port unless you are sure what you are doing. RRRRDDCCAACCHHEEDD,, TTHHEE CCAACCHHIINNGG DDAAEEMMOONN For very big setups, updating thousands of RRD files often becomes a serious IO problem. If you run into such problems, you might want to take a look at rrdcached, a caching daemon for RRDtool which may help you lessen the stress on your disks. SSEEEE AALLSSOO rrdcreate, rrdupdate, rrdgraph, rrddump, rrdfetch, rrdtune, rrdlast, rrdxport, rrdflushcached, rrdcached BBUUGGSS Bugs? Features! AAUUTTHHOORR Tobias Oetiker 1.4.7 2009-10-14 RRDTOOL(1) rrdtool-1.4.7/doc/rrdgraph_rpn.txt0000644000175300017510000004221711707501551014143 00000000000000RRDGRAPH_RPN(1) rrdtool RRDGRAPH_RPN(1) NNAAMMEE rrdgraph_rpn - About RPN Math in rrdtool graph SSYYNNOOPPSSIISS _R_P_N _e_x_p_r_e_s_s_i_o_n:=_v_n_a_m_e|_o_p_e_r_a_t_o_r|_v_a_l_u_e[,_R_P_N _e_x_p_r_e_s_s_i_o_n] DDEESSCCRRIIPPTTIIOONN If you have ever used a traditional HP calculator you already know RRPPNN (Reverse Polish Notation). The idea behind RRPPNN is that you have a stack and push your data onto this stack. Whenever you execute an operation, it takes as many elements from the stack as needed. Pushing is done implicitly, so whenever you specify a number or a variable, it gets pushed onto the stack automatically. At the end of the calculation there should be one and only one value left on the stack. This is the outcome of the function and this is what is put into the _v_n_a_m_e. For CCDDEEFF instructions, the stack is processed for each data point on the graph. VVDDEEFF instructions work on an entire data set in one run. Note, that currently VVDDEEFF instructions only support a limited list of functions. Example: "VDEF:maximum=mydata,MAXIMUM" This will set variable "maximum" which you now can use in the rest of your RRD script. Example: "CDEF:mydatabits=mydata,8,*" This means: push variable _m_y_d_a_t_a, push the number 8, execute the operator _*. The operator needs two elements and uses those to return one value. This value is then stored in _m_y_d_a_t_a_b_i_t_s. As you may have guessed, this instruction means nothing more than _m_y_d_a_t_a_b_i_t_s _= _m_y_d_a_t_a _* _8. The real power of RRPPNN lies in the fact that it is always clear in which order to process the input. For expressions like "a = b + 3 * 5" you need to multiply 3 with 5 first before you add _b to get _a. However, with parentheses you could change this order: "a = (b + 3) * 5". In RRPPNN, you would do "a = b, 3, +, 5, *" without the need for parentheses. OOPPEERRAATTOORRSS Boolean operators LLTT,, LLEE,, GGTT,, GGEE,, EEQQ,, NNEE Pop two elements from the stack, compare them for the selected condition and return 1 for true or 0 for false. Comparing an _u_n_k_n_o_w_n or an _i_n_f_i_n_i_t_e value will result in _u_n_k_n_o_w_n returned ... which will also be treated as false by the IIFF call. UUNN,, IISSIINNFF Pop one element from the stack, compare this to _u_n_k_n_o_w_n respectively to _p_o_s_i_t_i_v_e _o_r _n_e_g_a_t_i_v_e _i_n_f_i_n_i_t_y. Returns 1 for true or 0 for false. IIFF Pops three elements from the stack. If the element popped last is 0 (false), the value popped first is pushed back onto the stack, otherwise the value popped second is pushed back. This does, indeed, mean that any value other than 0 is considered to be true. Example: "A,B,C,IF" should be read as "if (A) then (B) else (C)" Comparing values MMIINN,, MMAAXX Pops two elements from the stack and returns the smaller or larger, respectively. Note that _i_n_f_i_n_i_t_e is larger than anything else. If one of the input numbers is _u_n_k_n_o_w_n then the result of the operation will be _u_n_k_n_o_w_n too. LLIIMMIITT Pops two elements from the stack and uses them to define a range. Then it pops another element and if it falls inside the range, it is pushed back. If not, an _u_n_k_n_o_w_n is pushed. The range defined includes the two boundaries (so: a number equal to one of the boundaries will be pushed back). If any of the three numbers involved is either _u_n_k_n_o_w_n or _i_n_f_i_n_i_t_e this function will always return an _u_n_k_n_o_w_n Example: "CDEF:a=alpha,0,100,LIMIT" will return _u_n_k_n_o_w_n if alpha is lower than 0 or if it is higher than 100. Arithmetics ++,, --,, **,, //,, %% Add, subtract, multiply, divide, modulo AADDDDNNAANN NAN-safe addition. If one parameter is NAN/UNKNOWN it'll be treated as zero. If both parameters are NAN/UNKNOWN, NAN/UNKNOWN will be returned. SSIINN,, CCOOSS,, LLOOGG,, EEXXPP,, SSQQRRTT Sine and cosine (input in radians), log and exp (natural logarithm), square root. AATTAANN Arctangent (output in radians). AATTAANN22 Arctangent of y,x components (output in radians). This pops one element from the stack, the x (cosine) component, and then a second, which is the y (sine) component. It then pushes the arctangent of their ratio, resolving the ambiguity between quadrants. Example: "CDEF:angle=Y,X,ATAN2,RAD2DEG" will convert "X,Y" components into an angle in degrees. FFLLOOOORR,, CCEEIILL Round down or up to the nearest integer. DDEEGG22RRAADD,, RRAADD22DDEEGG Convert angle in degrees to radians, or radians to degrees. AABBSS Take the absolute value. Set Operations SSOORRTT,, RREEVV Pop one element from the stack. This is the _c_o_u_n_t of items to be sorted (or reversed). The top _c_o_u_n_t of the remaining elements are then sorted (or reversed) in place on the stack. Example: "CDEF:x=v1,v2,v3,v4,v5,v6,6,SORT,POP,5,REV,POP,+,+,+,4,/" will compute the average of the values v1 to v6 after removing the smallest and largest. AAVVGG Pop one element (_c_o_u_n_t) from the stack. Now pop _c_o_u_n_t elements and build the average, ignoring all UNKNOWN values in the process. Example: "CDEF:x=a,b,c,d,4,AVG" TTRREENNDD,, TTRREENNDDNNAANN Create a "sliding window" average of another data series. Usage: CDEF:smoothed=x,1800,TREND This will create a half-hour (1800 second) sliding window average of x. The average is essentially computed as shown here: +---!---!---!---!---!---!---!---!---> now delay t0 <---------------> delay t1 <---------------> delay t2 <---------------> Value at sample (t0) will be the average between (t0-delay) and (t0) Value at sample (t1) will be the average between (t1-delay) and (t1) Value at sample (t2) will be the average between (t2-delay) and (t2) TRENDNAN is - in contrast to TREND - NAN-safe. If you use TREND and one source value is NAN the complete sliding window is affected. The TRENDNAN operation ignores all NAN-values in a sliding window and computes the average of the remaining values. PPRREEDDIICCTT,, PPRREEDDIICCTTSSIIGGMMAA Create a "sliding window" average/sigma of another data series, that also shifts the data series by given amounts of of time as well Usage - explicit stating shifts: CDEF:predict=,...,,n,,x,PREDICT CDEF:sigma=,...,,n,,x,PREDICTSIGMA Usage - shifts defined as a base shift and a number of time this is applied CDEF:predict=,-n,,x,PREDICT CDEF:sigma=,-n,,x,PREDICTSIGMA Example: CDEF:predict=172800,86400,2,1800,x,PREDICT This will create a half-hour (1800 second) sliding window average/sigma of x, that average is essentially computed as shown here: +---!---!---!---!---!---!---!---!---!---!---!---!---!---!---!---!---!---> now shift 1 t0 <-----------------------> window <---------------> shift 2 <-----------------------------------------------> window <---------------> shift 1 t1 <-----------------------> window <---------------> shift 2 <-----------------------------------------------> window <---------------> Value at sample (t0) will be the average between (t0-shift1-window) and (t0-shift1) and between (t0-shift2-window) and (t0-shift2) Value at sample (t1) will be the average between (t1-shift1-window) and (t1-shift1) and between (t1-shift2-window) and (t1-shift2) The function is by design NAN-safe. This also allows for extrapolation into the future (say a few days) - you may need to define the data series whit the optional start= parameter, so that the source data series has enough data to provide prediction also at the beginning of a graph... Here an example, that will create a 10 day graph that also shows the prediction 3 days into the future with its uncertainty value (as defined by avg+-4*sigma) This also shows if the prediction is exceeded at a certain point. rrdtool graph image.png --imgformat=PNG \ --start=-7days --end=+3days --width=1000 --height=200 --alt-autoscale-max \ DEF:value=value.rrd:value:AVERAGE:start=-14days \ LINE1:value#ff0000:value \ CDEF:predict=86400,-7,1800,value,PREDICT \ CDEF:sigma=86400,-7,1800,value,PREDICTSIGMA \ CDEF:upper=predict,sigma,3,*,+ \ CDEF:lower=predict,sigma,3,*,- \ LINE1:predict#00ff00:prediction \ LINE1:upper#0000ff:upper\ certainty\ limit \ LINE1:lower#0000ff:lower\ certainty\ limit \ CDEF:exceeds=value,UN,0,value,lower,upper,LIMIT,UN,IF \ TICK:exceeds#aa000080:1 Note: Experience has shown that a factor between 3 and 5 to scale sigma is a good discriminator to detect abnormal behavior. This obviously depends also on the type of data and how "noisy" the data series is. This prediction can only be used for short term extrapolations - say a few days into the future- Special values UUNNKKNN Pushes an unknown value on the stack IINNFF,, NNEEGGIINNFF Pushes a positive or negative infinite value on the stack. When such a value is graphed, it appears at the top or bottom of the graph, no matter what the actual value on the y-axis is. PPRREEVV Pushes an _u_n_k_n_o_w_n value if this is the first value of a data set or otherwise the result of this CCDDEEFF at the previous time step. This allows you to do calculations across the data. This function cannot be used in VVDDEEFF instructions. PPRREEVV((vvnnaammee)) Pushes an _u_n_k_n_o_w_n value if this is the first value of a data set or otherwise the result of the vname variable at the previous time step. This allows you to do calculations across the data. This function cannot be used in VVDDEEFF instructions. CCOOUUNNTT Pushes the number 1 if this is the first value of the data set, the number 2 if it is the second, and so on. This special value allows you to make calculations based on the position of the value within the data set. This function cannot be used in VVDDEEFF instructions. Time Time inside RRDtool is measured in seconds since the epoch. The epoch is defined to be "Thu Jan 1 00:00:00 UTC 1970". NNOOWW Pushes the current time on the stack. TTIIMMEE Pushes the time the currently processed value was taken at onto the stack. LLTTIIMMEE Takes the time as defined by TTIIMMEE, applies the time zone offset valid at that time including daylight saving time if your OS supports it, and pushes the result on the stack. There is an elaborate example in the examples section below on how to use this. Processing the stack directly DDUUPP,, PPOOPP,, EEXXCC Duplicate the top element, remove the top element, exchange the two top elements. VVAARRIIAABBLLEESS These operators work only on VVDDEEFF statements. Note that currently ONLY these work for VVDDEEFF. MAXIMUM, MINIMUM, AVERAGE Return the corresponding value, MAXIMUM and MINIMUM also return the first occurrence of that value in the time component. Example: "VDEF:avg=mydata,AVERAGE" STDEV Returns the standard deviation of the values. Example: "VDEF:stdev=mydata,STDEV" LAST, FIRST Return the last/first non-nan or infinite value for the selected data stream, including its timestamp. Example: "VDEF:first=mydata,FIRST" TOTAL Returns the rate from each defined time slot multiplied with the step size. This can, for instance, return total bytes transferred when you have logged bytes per second. The time component returns the number of seconds. Example: "VDEF:total=mydata,TOTAL" PERCENT, PERCENTNAN This should follow a DDEEFF or CCDDEEFF _v_n_a_m_e. The _v_n_a_m_e is popped, another number is popped which is a certain percentage (0..100). The data set is then sorted and the value returned is chosen such that _p_e_r_c_e_n_t_a_g_e percent of the values is lower or equal than the result. For PERCENTNAN _U_n_k_n_o_w_n values are ignored, but for PERCENT _U_n_k_n_o_w_n values are considered lower than any finite number for this purpose so if this operator returns an _u_n_k_n_o_w_n you have quite a lot of them in your data. IInnffinite numbers are lesser, or more, than the finite numbers and are always more than the _U_n_k_n_o_w_n numbers. (NaN < -INF < finite values < INF) Example: "VDEF:perc95=mydata,95,PERCENT" "VDEF:percnan95=mydata,95,PERCENTNAN" LSLSLOPE, LSLINT, LSLCORREL Return the parameters for a LLeast SSquares LLine _(_y _= _m_x _+_b_) which approximate the provided dataset. LSLSLOPE is the slope _(_m_) of the line related to the COUNT position of the data. LSLINT is the y-intercept _(_b_), which happens also to be the first data point on the graph. LSLCORREL is the Correlation Coefficient (also know as Pearson's Product Moment Correlation Coefficient). It will range from 0 to +/-1 and represents the quality of fit for the approximation. Example: "VDEF:slope=mydata,LSLSLOPE" SSEEEE AALLSSOO rrdgraph gives an overview of how rrrrddttooooll ggrraapphh works. rrdgraph_data describes DDEEFF,CCDDEEFF and VVDDEEFF in detail. rrdgraph_rpn describes the RRPPNN language used in the ??DDEEFF statements. rrdgraph_graph page describes all of the graph and print functions. Make sure to read rrdgraph_examples for tips&tricks. AAUUTTHHOORR Program by Tobias Oetiker This manual page by Alex van den Bogaerdt with corrections and/or additions by several people 1.4.7 2012-01-18 RRDGRAPH_RPN(1) rrdtool-1.4.7/doc/rrdfirst.txt0000644000175300017510000000203311707501551013302 00000000000000RRDFIRST(1) rrdtool RRDFIRST(1) NNAAMMEE rrdfirst - Return the date of the first data sample in an RRA within an RRD SSYYNNOOPPSSIISS rrrrddttooooll ffiirrsstt _f_i_l_e_n_a_m_e [_-_-_r_r_a_i_n_d_e_x _n_u_m_b_e_r] DDEESSCCRRIIPPTTIIOONN The ffiirrsstt function returns the UNIX timestamp of the first data sample entered into the specified RRA of the RRD file. _f_i_l_e_n_a_m_e The name of the RRRRDD that contains the data. _-_-_r_r_a_i_n_d_e_x _n_u_m_b_e_r The index number of the RRRRAA that is to be examined. If not specified, the index defaults to zero. RRRRAA index numbers can be determined through rrrrddttooooll iinnffoo. AAUUTTHHOORR Burton Strauss 1.4.7 2008-03-15 RRDFIRST(1) rrdtool-1.4.7/doc/rpntutorial.txt0000644000175300017510000001731511707501551014037 00000000000000RPNTUTORIAL(1) rrdtool RPNTUTORIAL(1) NNAAMMEE rpntutorial - Reading RRDtool RPN Expressions by Steve Rader DDEESSCCRRIIPPTTIIOONN This tutorial should help you get to grips with RRDtool RPN expressions as seen in CDEF arguments of RRDtool graph. RReeaaddiinngg CCoommppaarriissoonn OOppeerraattoorrss 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, then 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 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. RReeaaddiinngg tthhee IIFF OOppeerraattoorr 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 bizarre 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. SSoommee EExxaammpplleess While compound expressions can look overly complex, they can be considered elegantly simple. To quickly comprehend RPN expressions, you must know 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 and is replaced by 5. This results in the stack "1,5,+". Finally, "1,5,+" is evaluated resulting in the answer 6. For convenience, 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 conveniently 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, 7000,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 ( lets call this 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 with it's verbose description again and--voila!--you have an easily readable description of the expression: if input > 10 then 10 else input Finally, let's go back to 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) EExxeerrcciisseess 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 traditional 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. Parentheses 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 was 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.) AAUUTTHHOORR Steve Rader 1.4.7 2009-12-08 RPNTUTORIAL(1) rrdtool-1.4.7/doc/rrdxport.txt0000644000175300017510000001555411707501551013343 00000000000000RRDXPORT(1) rrdtool RRDXPORT(1) NNAAMMEE rrdxport - Export data in XML format based on data from one or several RRD SSYYNNOOPPSSIISS rrrrddttooooll xxppoorrtt [--ss|----ssttaarrtt _s_e_c_o_n_d_s] [--ee|----eenndd _s_e_c_o_n_d_s] [--mm|----mmaaxxrroowwss _r_o_w_s] [----sstteepp _v_a_l_u_e] [----jjssoonn] [----ddaaeemmoonn _a_d_d_r_e_s_s] [DDEEFF::_v_n_a_m_e==_r_r_d::_d_s_-_n_a_m_e::_C_F] [CCDDEEFF::_v_n_a_m_e==_r_p_n_-_e_x_p_r_e_s_s_i_o_n] [XXPPOORRTT::_v_n_a_m_e[::_l_e_g_e_n_d]] DDEESSCCRRIIPPTTIIOONN The xxppoorrtt function's main purpose is to write an XML formatted representation of the data stored in one or several RRRRDDs. It can also extract numerical reports. If no _X_P_O_R_T statements are found, there will be no output. --ss|----ssttaarrtt _s_e_c_o_n_d_s (default end-1day) The time when the exported range should begin. 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 printed. See also AT-STYLE TIME SPECIFICATION section in the _r_r_d_f_e_t_c_h documentation for a detailed explanation on how to specify time. --ee|----eenndd _s_e_c_o_n_d_s (default now) The time when the exported range should end. Time in seconds since epoch. See also AT-STYLE TIME SPECIFICATION section in the _r_r_d_f_e_t_c_h documentation for a detailed explanation of ways to specify time. --mm|----mmaaxxrroowwss _r_o_w_s (default 400 rows) This works like the --ww|----wwiiddtthh parameter of _r_r_d_g_r_a_p_h. In fact it is exactly the same, but the parameter was renamed to describe its purpose in this module. See _r_r_d_g_r_a_p_h documentation for details. ----sstteepp _v_a_l_u_e (default automatic) See rrdgraph documentation. ----ddaaeemmoonn _a_d_d_r_e_s_s Address of the rrdcached daemon. If specified, a "flush" command is sent to the server before reading the RRD files. This allows rrrrddttooooll to return fresh data even if the daemon is configured to cache values for a long time. For a list of accepted formats, see the --ll option in the rrdcached manual. rrdtool xport --daemon unix:/var/run/rrdcached.sock ... ----jjssoonn produce json formated output (instead of xml) ----eennuummddss The generated xml should contain the data values in enumerated tags. valval DDEEFF::_v_n_a_m_e==_r_r_d::_d_s_-_n_a_m_e::_C_F See _r_r_d_g_r_a_p_h documentation. CCDDEEFF::_v_n_a_m_e==_r_p_n_-_e_x_p_r_e_s_s_i_o_n See _r_r_d_g_r_a_p_h documentation. XXPPOORRTT::_v_n_a_m_e::::_l_e_g_e_n_d At least one _X_P_O_R_T statement should be present. The values referenced by _v_n_a_m_e are printed. Optionally add a legend. OOuuttppuutt ffoorrmmaatt The output is enclosed in an xxppoorrtt element and contains two blocks. The first block is enclosed by a mmeettaa element and contains some meta data. The second block is enclosed by a ddaattaa element and contains the data rows. Let's assume that the _x_p_o_r_t command looks like this: rrdtool xport \ --start now-1h --end now \ DEF:xx=host-inout.lo.rrd:output:AVERAGE \ DEF:yy=host-inout.lo.rrd:input:AVERAGE \ CDEF:aa=xx,yy,+,8,* \ XPORT:xx:"out bytes" \ XPORT:aa:"in and out bits" The resulting meta data section is (the values will depend on the RRD characteristics): 1020611700 300 1020615600 14 2 out bytes in and out bits The resulting data section is: 10206117003.4000000000e+005.4400000000e+01 10206120003.4000000000e+005.4400000000e+01 10206123003.4000000000e+005.4400000000e+01 10206126003.4113333333e+005.4581333333e+01 10206129003.4000000000e+005.4400000000e+01 10206132003.4000000000e+005.4400000000e+01 10206135003.4000000000e+005.4400000000e+01 10206138003.4000000000e+005.4400000000e+01 10206141003.4000000000e+005.4400000000e+01 10206144003.4000000000e+005.4400000000e+01 10206147003.7333333333e+005.9733333333e+01 10206150003.4000000000e+005.4400000000e+01 10206153003.4000000000e+005.4400000000e+01 1020615600NaNNaN EEXXAAMMPPLLEE 11 rrdtool xport \ DEF:out=if1-inouts.rrd:outoctets:AVERAGE \ XPORT:out:"out bytes" EEXXAAMMPPLLEE 22 rrdtool xport \ DEF:out1=if1-inouts.rrd:outoctets:AVERAGE \ DEF:out2=if2-inouts.rrd:outoctets:AVERAGE \ CDEF:sum=out1,out2,+ \ XPORT:out1:"if1 out bytes" \ XPORT:out2:"if2 out bytes" \ XPORT:sum:"output sum" EENNVVIIRROONNMMEENNTT VVAARRIIAABBLLEESS The following environment variables may be used to change the behavior of "rrdtool xport": RRRRDDCCAACCHHEEDD__AADDDDRREESSSS If this environment variable is set it will have the same effect as specifying the "--daemon" option on the command line. If both are present, the command line argument takes precedence. AAUUTTHHOORR Tobias Oetiker 1.4.7 2012-01-16 RRDXPORT(1) rrdtool-1.4.7/doc/rrdthreads.txt0000644000175300017510000001103311707501551013605 00000000000000RRDTHREADS(1) rrdtool RRDTHREADS(1) NNAAMMEE rrdthreads - Provisions for linking the RRD library to use in multi-threaded programs SSYYNNOOPPSSIISS Using librrd in multi-threaded programs requires some extra precautions, as the RRD library in its original form was not thread- safe at all. This document describes requirements and pitfalls on the way to use the multi-threaded version of librrd in your own programs. It also gives hints for future RRD development to keep the library thread-safe. Currently only some RRD operations are implemented in a thread-safe way. They all end in the usual ""_r"" suffix. DDEESSCCRRIIPPTTIIOONN In order to use librrd in multi-threaded programs you must: · Link with _l_i_b_r_r_d___t_h instead of _l_i_b_r_r_d (use "-lrrd_th" when linking) · Use the ""_r"" functions instead of the normal API-functions · Do not use any at-style time specifications. Parsing of such time specifications is terribly non-thread-safe. · Never use non *"_r" functions unless it is explicitly documented that the function is tread-safe. · Every thread SHOULD call "rrd_get_context()" before its first call to any "librrd_th" function in order to set up thread specific data. This is not strictly required, but it is the only way to test if memory allocation can be done by this function. Otherwise the program may die with a SIGSEGV in a low-memory situation. · Always call "rrd_error_clear()" before any call to the library. Otherwise the call might fail due to some earlier error. NNOOTTEESS FFOORR RRRRDD CCOONNTTRRIIBBUUTTOORRSS Some precautions must be followed when developing RRD from now on: · Only use thread-safe functions in library code. Many often used libc functions aren't thread-safe. Take care in the following situations or when using the following library functions: · Direct calls to "strerror()" must be avoided: use "rrd_strerror()" instead, it provides a per-thread error message. · The "getpw*", "getgr*", "gethost*" function families (and some more "get*" functions) are not thread-safe: use the *"_r" variants · Time functions: "asctime", "ctime", "gmtime", "localtime": use *"_r" variants · "strtok": use "strtok_r" · "tmpnam": use "tmpnam_r" · Many others (lookup documentation) · A header file named _r_r_d___i_s___t_h_r_e_a_d___s_a_f_e_._h is provided that works with the GNU C-preprocessor to "poison" some of the most common non-thread-safe functions using the "#pragma GCC poison" directive. Just include this header in source files you want to keep thread- safe. · Do not introduce global variables! If you really, really have to use a global variable you may add a new field to the "rrd_context" structure and modify _r_r_d___e_r_r_o_r_._c, _r_r_d___t_h_r_e_a_d___s_a_f_e_._c and _r_r_d___n_o_n___t_h_r_e_a_d___s_a_f_e_._c · Do not use "getopt" or "getopt_long" in *"_r" (neither directly nor indirectly). "getopt" uses global variables and behaves badly in a multi- threaded application when called concurrently. Instead provide a *_r function taking all options as function parameters. You may provide argc and **argv arguments for variable length argument lists. See "rrd_update_r" as an example. · Do not use the "rrd_parsetime" function! It uses lots of global variables. You may use it in functions not designed to be thread-safe, like in functions wrapping the "_r" version of some operation (e.g., "rrd_create", but not in "rrd_create_r") CCUURRRREENNTTLLYY IIMMPPLLEEMMEENNTTEEDD TTHHRREEAADD SSAAFFEE FFUUNNCCTTIIOONNSS Currently there exist thread-safe variants of "rrd_update", "rrd_create", "rrd_dump", "rrd_info", "rrd_last", and "rrd_fetch". AAUUTTHHOORR Peter Stamfest 1.4.7 2008-06-08 RRDTHREADS(1) rrdtool-1.4.7/doc/rrdgraph_graph.txt0000644000175300017510000005053211707501551014444 00000000000000RRDGRAPH_GRAPH(1) rrdtool RRDGRAPH_GRAPH(1) NNAAMMEE rrdgraph_graph - rrdtool graph command reference SSYYNNOOPPSSIISS PPRRIINNTT::_v_n_a_m_e::_f_o_r_m_a_t GGPPRRIINNTT::_v_n_a_m_e::_f_o_r_m_a_t CCOOMMMMEENNTT::_t_e_x_t VVRRUULLEE::_t_i_m_e##_c_o_l_o_r[::_l_e_g_e_n_d][::ddaasshheess[==_o_n___s[,_o_f_f___s[,_o_n___s,_o_f_f___s]...]][::ddaasshh--ooffffsseett==_o_f_f_s_e_t]] HHRRUULLEE::_v_a_l_u_e##_c_o_l_o_r[::_l_e_g_e_n_d][::ddaasshheess[==_o_n___s[,_o_f_f___s[,_o_n___s,_o_f_f___s]...]][::ddaasshh--ooffffsseett==_o_f_f_s_e_t]] LLIINNEE[_w_i_d_t_h]::_v_a_l_u_e[##_c_o_l_o_r][::[_l_e_g_e_n_d][::SSTTAACCKK]][::ddaasshheess[==_o_n___s[,_o_f_f___s[,_o_n___s,_o_f_f___s]...]][::ddaasshh--ooffffsseett==_o_f_f_s_e_t]] AARREEAA::_v_a_l_u_e[##_c_o_l_o_r][::[_l_e_g_e_n_d][::SSTTAACCKK]] TTIICCKK::_v_n_a_m_e##_r_r_g_g_b_b[_a_a][::_f_r_a_c_t_i_o_n[::_l_e_g_e_n_d]] SSHHIIFFTT::_v_n_a_m_e::_o_f_f_s_e_t TTEEXXTTAALLIIGGNN::{lleefftt|rriigghhtt|jjuussttiiffiieedd|cceenntteerr} PPRRIINNTT::_v_n_a_m_e::_C_F::_f_o_r_m_a_t (deprecated) GGPPRRIINNTT::_v_n_a_m_e::_C_F::_f_o_r_m_a_t (deprecated) SSTTAACCKK::_v_n_a_m_e##_c_o_l_o_r[::_l_e_g_e_n_d] (deprecated) DDEESSCCRRIIPPTTIIOONN These instructions allow you to generate your image or report. If you don't use any graph elements, no graph is generated. Similarly, no report is generated if you don't use print options. PPRRIINNTT _PP_RR_II_NN_TT_::_v_n_a_m_e_::_f_o_r_m_a_t_[_::_ss_tt_rr_ff_tt_ii_mm_ee_] Depending on the context, either the value component or the time component of a VVDDEEFF is printed using _f_o_r_m_a_t. It is an error to specify a _v_n_a_m_e generated by a DDEEFF or CCDDEEFF. Any text in _f_o_r_m_a_t is printed literally with one exception: The percent character introduces a formatter string. This string can be: For printing values: %%%% just prints a literal '%' character %%##..##llee prints numbers like 1.2346e+04. The optional integers # denote field width and decimal precision. %%##..##llff prints numbers like 12345.6789, with optional field width and precision. %%ss place this after %%llee, %%llff or %%llgg. This will be replaced by the appropriate SI magnitude unit and the value will be scaled accordingly (123456 -> 123.456 k). %%SS is similar to %%ss. It does, however, use a previously defined magnitude unit. If there is no such unit yet, it tries to define one (just like %%ss) unless the value is zero, in which case the magnitude unit stays undefined. Thus, formatter strings using %%SS and no %%ss will all use the same magnitude unit except for zero values. If you PRINT a VDEF value, you can also print the time associated with it by appending the string ::ssttrrffttiimmee to the format. Note that RRDtool uses the strftime function of your OSs C library. This means that the conversion specifier may vary. Check the manual page if you are uncertain. The following is a list of conversion specifiers usually supported across the board. %%aa The abbreviated weekday name according to the current locale. %%AA The full weekday name according to the current locale. %%bb The abbreviated month name according to the current locale. %%BB The full month name according to the current locale. %%cc The preferred date and time representation for the current locale. %%dd The day of the month as a decimal number (range 01 to 31). %%HH The hour as a decimal number using a 24-hour clock (range 00 to 23). %%II The hour as a decimal number using a 12-hour clock (range 01 to 12). %%jj The day of the year as a decimal number (range 001 to 366). %%mm The month as a decimal number (range 01 to 12). %%MM The minute as a decimal number (range 00 to 59). %%pp Either `AM' or `PM' according to the given time value, or the corresponding strings for the current locale. Noon is treated as `pm' and midnight as `am'. Note that in many locales and `pm' notation is unsupported and in such cases %p will return an empty string. %%ss The second as a decimal number (range 00 to 61). %%SS The seconds since the epoch (1.1.1970) (libc dependent non standard!) %%UU The week number of the current year as a decimal number, range 00 to 53, starting with the first Sunday as the first day of week 01. See also %V and %W. %%VV The ISO 8601:1988 week number of the current year as a decimal number, range 01 to 53, where week 1 is the first week that has at least 4 days in the current year, and with Monday as the first day of the week. See also %U and %W. %%ww The day of the week as a decimal, range 0 to 6, Sunday being 0. See also %u. %%WW The week number of the current year as a decimal number, range 00 to 53, starting with the first Monday as the first day of week 01. %%xx The preferred date representation for the current locale without the time. %%XX The preferred time representation for the current locale without the date. %%yy The year as a decimal number without a century (range 00 to 99). %%YY The year as a decimal number including the century. %%ZZ The time zone or name or abbreviation. %%%% A literal `%' character. _PP_RR_II_NN_TT_::_v_n_a_m_e_::_C_F_::_f_o_r_m_a_t _D_e_p_r_e_c_a_t_e_d_. _U_s_e _t_h_e _n_e_w _f_o_r_m _o_f _t_h_i_s _c_o_m_m_a_n_d _i_n _n_e_w _s_c_r_i_p_t_s_. The first form of this command is to be used with CCDDEEFF _v_n_a_m_es. GGRRAAPPHH _GG_PP_RR_II_NN_TT_::_v_n_a_m_e_::_f_o_r_m_a_t This is the same as "PRINT", but printed inside the graph. _GG_PP_RR_II_NN_TT_::_v_n_a_m_e_::_C_F_::_f_o_r_m_a_t _D_e_p_r_e_c_a_t_e_d_. _U_s_e _t_h_e _n_e_w _f_o_r_m _o_f _t_h_i_s _c_o_m_m_a_n_d _i_n _n_e_w _s_c_r_i_p_t_s_. This is the same as "PRINT", but printed inside the graph. _CC_OO_MM_MM_EE_NN_TT_::_t_e_x_t Text is printed literally in the legend section of the graph. Note that in RRDtool 1.2 you have to escape colons in COMMENT text in the same way you have to escape them in **PPRRIINNTT commands by writing ''\\::''. _VV_RR_UU_LL_EE_::_t_i_m_e_##_c_o_l_o_r_[_::_l_e_g_e_n_d_]_[_::_dd_aa_ss_hh_ee_ss_[_==_o_n___s_[_,_o_f_f___s_[_,_o_n___s_,_o_f_f___s_]_._._._]_]_[_::_dd_aa_ss_hh_--_oo_ff_ff_ss_ee_tt_==_o_f_f_s_e_t_]_] Draw a vertical line at _t_i_m_e. Its color is composed from three hexadecimal numbers specifying the rgb color components (00 is off, FF is maximum) red, green and blue followed by an optional alpha. Optionally, a legend box and string is printed in the legend section. _t_i_m_e may be a number or a variable from a VVDDEEFF. It is an error to use _v_n_a_m_es from DDEEFF or CCDDEEFF here. Dashed lines can be drawn using the ddaasshheess modifier. See LLIINNEE for more details. _HH_RR_UU_LL_EE_::_v_a_l_u_e_##_c_o_l_o_r_[_::_l_e_g_e_n_d_]_[_::_dd_aa_ss_hh_ee_ss_[_==_o_n___s_[_,_o_f_f___s_[_,_o_n___s_,_o_f_f___s_]_._._._]_]_[_::_dd_aa_ss_hh_--_oo_ff_ff_ss_ee_tt_==_o_f_f_s_e_t_]_] Draw a horizontal line at _v_a_l_u_e. HRULE acts much like LINE except that will have no effect on the scale of the graph. If a HRULE is outside the graphing area it will just not be visible. _LL_II_NN_EE_[_w_i_d_t_h_]_::_v_a_l_u_e_[_##_c_o_l_o_r_]_[_::_[_l_e_g_e_n_d_]_[_::_SS_TT_AA_CC_KK_]_]_[_::_dd_aa_ss_hh_ee_ss_[_==_o_n___s_[_,_o_f_f___s_[_,_o_n___s_,_o_f_f___s_]_._._._]_]_[_::_dd_aa_ss_hh_--_oo_ff_ff_ss_ee_tt_==_o_f_f_s_e_t_]_] Draw a line of the specified width onto the graph. _w_i_d_t_h can be a floating point number. If the color is not specified, the drawing is done 'invisibly'. This is useful when stacking something else on top of this line. Also optional is the legend box and string which will be printed in the legend section if specified. The vvaalluuee can be generated by DDEEFF, VVDDEEFF, and CCDDEEFF. If the optional SSTTAACCKK modifier is used, this line is stacked on top of the previous element which can be a LLIINNEE or an AARREEAA. The ddaasshheess modifier enables dashed line style. Without any further options a symmetric dashed line with a segment length of 5 pixels will be drawn. The dash pattern can be changed if the ddaasshheess== parameter is followed by either one value or an even number (1, 2, 4, 6, ...) of positive values. Each value provides the length of alternate _o_n___s and _o_f_f___s portions of the stroke. The ddaasshh--ooffffsseett parameter specifies an _o_f_f_s_e_t into the pattern at which the stroke begins. When you do not specify a color, you cannot specify a legend. Should you want to use STACK, use the "LINEx:::STACK" form. _AA_RR_EE_AA_::_v_a_l_u_e_[_##_c_o_l_o_r_]_[_::_[_l_e_g_e_n_d_]_[_::_SS_TT_AA_CC_KK_]_] See LLIINNEE, however the area between the x-axis and the line will be filled. _TT_II_CC_KK_::_v_n_a_m_e_##_r_r_g_g_b_b_[_a_a_]_[_::_f_r_a_c_t_i_o_n_[_::_l_e_g_e_n_d_]_] Plot a tick mark (a vertical line) for each value of _v_n_a_m_e that is non- zero and not *UNKNOWN*. The _f_r_a_c_t_i_o_n argument specifies the length of the tick mark as a fraction of the y-axis; the default value is 0.1 (10% of the axis). Note that the color specification is not optional. The TICK marks normally start at the lower edge of the graphing area. If the fraction is negative they start at the upper border of the graphing area. _SS_HH_II_FF_TT_::_v_n_a_m_e_::_o_f_f_s_e_t Using this command RRRRDDttooooll will graph the following elements with the specified offset. For instance, you can specify an offset of ( 7*24*60*60 = ) 604'800 seconds to "look back" one week. Make sure to tell the viewer of your graph you did this ... As with the other graphing elements, you can specify a number or a variable here. _TT_EE_XX_TT_AA_LL_II_GG_NN_::_{_ll_ee_ff_tt_|_rr_ii_gg_hh_tt_|_jj_uu_ss_tt_ii_ff_ii_ee_dd_|_cc_ee_nn_tt_ee_rr_} Labels are placed below the graph. When they overflow to the left, they wrap to the next line. By default, lines are justified left and right. The TTEEXXTTAALLIIGGNN function lets you change this default. This is a command and not an option, so that you can change the default several times in your argument list. _SS_TT_AA_CC_KK_::_v_n_a_m_e_##_c_o_l_o_r_[_::_l_e_g_e_n_d_] _D_e_p_r_e_c_a_t_e_d_. _U_s_e _t_h_e _SS_TT_AA_CC_KK _m_o_d_i_f_i_e_r_s _o_n _t_h_e _o_t_h_e_r _c_o_m_m_a_n_d_s _i_n_s_t_e_a_d_! SSoommee nnootteess oonn ssttaacckkiinngg When stacking, an element is not placed above the X-axis but rather on top of the previous element. There must be something to stack upon. You can use an iinnvviissiibbllee LINE or AREA to stacked upon. An uunnkknnoowwnn value makes the entire stack unknown from that moment on. You don't know where to begin (the unknown value) and therefore do not know where to end. If you want to make sure you will be displaying a certain variable, make sure never to stack upon the unknown value. Use a CDEF instruction with IIFF and UUNN to do so. NNOOTTEESS oonn lleeggeenndd aarrgguummeennttss EEssccaappiinngg tthhee ccoolloonn A colon ':' in a _l_e_g_e_n_d argument will mark the end of the legend. To enter a ':' as part of a legend, the colon must be escaped with a backslash '\:'. Beware that many environments process backslashes themselves, so it may be necessary to write two backslashes in order to one being passed onto rrd_graph. SSttrriinngg FFoorrmmaattttiinngg The text printed below the actual graph can be formatted by appending special escape characters at the end of a text. When ever such a character occurs, all pending text is pushed onto the graph according to the character specified. Valid markers are: \\jj for justified, \\ll for left aligned, \\rr for right aligned, and \\cc for centered. In the next section there is an example showing how to use centered formatting. \\nn is a valid alias for \\ll since incomplete parsing in earlier versions of RRDtool lead to this behavior and a number of people has been using it. Normally there are two space characters inserted between every two items printed into the graph. The space following a string can be suppressed by putting a \\gg at the end of the string. The \\gg also ignores any space inside the string if it is at the very end of the string. This can be used in connection with %%ss to suppress empty unit strings. GPRINT:a:MAX:%lf%s\g A special case is COMMENT:\\ss which inserts some additional vertical space before placing the next row of legends. If you want to have left and right aligned legends on the same line use COMMENT:\\uu to go one line back like this: COMMENT:left\l COMMENT:\u COMMENT:right\r There is also a 'nop' control for situations where you want a string to actually end in a backslash character sequence \\.. COMMENT:OS\2\. When using a proportional font in your graph, the tab characters or the sequence \\tt will line-up legend elements. Note that the tabs inserted are relative to the start of the current legend element! Since RRDtool 1.3 is using Pango for rending text, you can use Pango markup. Pango uses the xml ssppaann tags for inline formatting instructions.: A simple example of a marked-up string might be: Blue text is cool! The complete list of attributes for the span tag (taken from the pango documentation): ffoonntt__ddeesscc A font description string, such as "Sans Italic 12"; note that any other span attributes will override this description. So if you have "Sans Italic" and also a style="normal" attribute, you will get Sans normal, not italic. ffoonntt__ffaammiillyy A font family name ffaaccee Synonym for font_family ssiizzee Font size in 1024ths of a point, or one of the absolute sizes 'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', or one of the relative sizes 'smaller' or 'larger'. If you want to specify a absolute size, it's usually easier to take advantage of the ability to specify a partial font description using 'font_desc'; you can use font_desc='12.5' rather than size='12800'. ssttyyllee One of 'normal', 'oblique', 'italic' wweeiigghhtt One of 'ultralight', 'light', 'normal', 'bold', 'ultrabold', 'heavy', or a numeric weight vvaarriiaanntt 'normal' or 'smallcaps' ssttrreettcchh One of 'ultracondensed', 'extracondensed', 'condensed', 'semicondensed', 'normal', 'semiexpanded', 'expanded', 'extraexpanded', 'ultraexpanded' ffoorreeggrroouunndd An RGB color specification such as '#00FF00' or a color name such as 'red' bbaacckkggrroouunndd An RGB color specification such as '#00FF00' or a color name such as 'red' uunnddeerrlliinnee One of 'none', 'single', 'double', 'low', 'error' uunnddeerrlliinnee__ccoolloorr The color of underlines; an RGB color specification such as '#00FF00' or a color name such as 'red' rriissee Vertical displacement, in 10000ths of an em. Can be negative for subscript, positive for superscript. ssttrriikkeetthhrroouugghh 'true' or 'false' whether to strike through the text ssttrriikkeetthhrroouugghh__ccoolloorr The color of crossed out lines; an RGB color specification such as '#00FF00' or a color name such as 'red' ffaallllbbaacckk 'true' or 'false' whether to enable fallback. If disabled, then characters will only be used from the closest matching font on the system. No fallback will be done to other fonts on the system that might contain the characters in the text. Fallback is enabled by default. Most applications should not disable fallback. llaanngg A language code, indicating the text language lleetttteerr__ssppaacciinngg Inter-letter spacing in 1024ths of a point. ggrraavviittyy One of 'south', 'east', 'north', 'west', 'auto'. ggrraavviittyy__hhiinntt One of 'natural', 'strong', 'line'. To save you some typing, there are also some shortcuts: bb Bold bbiigg Makes font relatively larger, equivalent to ii Italic ss Strike through ssuubb Subscript ssuupp Superscript ssmmaallll Makes font relatively smaller, equivalent to tttt Monospace font uu Underline SSEEEE AALLSSOO rrdgraph gives an overview of how rrrrddttooooll ggrraapphh works. rrdgraph_data describes DDEEFF,CCDDEEFF and VVDDEEFF in detail. rrdgraph_rpn describes the RRPPNN language used in the ??DDEEFF statements. rrdgraph_graph page describes all of the graph and print functions. Make sure to read rrdgraph_examples for tips&tricks. AAUUTTHHOORR Program by Tobias Oetiker This manual page by Alex van den Bogaerdt with corrections and/or additions by several people 1.4.7 2012-01-20 RRDGRAPH_GRAPH(1) rrdtool-1.4.7/doc/rrdfetch.txt0000644000175300017510000003262311707501551013254 00000000000000RRDFETCH(1) rrdtool RRDFETCH(1) NNAAMMEE rrdfetch - Fetch data from an RRD. SSYYNNOOPPSSIISS rrrrddttooooll ffeettcchh _f_i_l_e_n_a_m_e _C_F [----rreessoolluuttiioonn|--rr _r_e_s_o_l_u_t_i_o_n] [----ssttaarrtt|--ss _s_t_a_r_t] [----eenndd|--ee _e_n_d] [----ddaaeemmoonn _a_d_d_r_e_s_s] DDEESSCCRRIIPPTTIIOONN The ffeettcchh function is normally used internally by the graph function to get data from RRRRDDs. ffeettcchh will analyze the RRRRDD and try to retrieve the data in the resolution requested. The data fetched is printed to stdout. _*_U_N_K_N_O_W_N_* data is often represented by the string "NaN" depending on your OS's printf function. _f_i_l_e_n_a_m_e the name of the RRRRDD you want to fetch the data from. _C_F the consolidation function that is applied to the data you want to fetch (AVERAGE,MIN,MAX,LAST) ----rreessoolluuttiioonn|--rr _r_e_s_o_l_u_t_i_o_n (default is the highest resolution) the interval you want the values to have (seconds per value). rrrrddffeettcchh will try to match your request, but it will return data even if no absolute match is possible. NNBB.. See note below. ----ssttaarrtt|--ss _s_t_a_r_t (default end-1day) start of the time series. 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 the start time. ----eenndd|--ee _e_n_d (default now) the end of the time series in seconds since epoch. See also AT- STYLE TIME SPECIFICATION section for a detailed explanation of how to specify the end time. ----ddaaeemmoonn _a_d_d_r_e_s_s Address of the rrdcached daemon. If specified, a "flush" command is sent to the server before reading the RRD files. This allows rrrrddttooooll to return fresh data even if the daemon is configured to cache values for a long time. For a list of accepted formats, see the --ll option in the rrdcached manual. rrdtool fetch --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd AVERAGE RREESSOOLLUUTTIIOONN IINNTTEERRVVAALL In order to get RRDtool to fetch anything other than the finest resolution RRA bbootthh the start and end time must be specified on boundaries that are multiples of the desired 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 to fetch the 15 minute average data for the last hour. You might try rrdtool fetch subdata.rrd AVERAGE -r 900 -s -1h However, this will almost always result in a time series that is NNOOTT 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 1. both start and end time are a multiple of 900 2. both start and end time are within the desired RRA So, if time now is called "t", do end time == int(t/900)*900, start time == end time - 1hour, resolution == 900. Using the bash shell, this could look be: TIME=$(date +%s) RRDRES=900 rrdtool fetch subdata.rrd AVERAGE -r $RRDRES \ -e $(($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"' AATT--SSTTYYLLEE TTIIMMEE SSPPEECCIIFFIICCAATTIIOONN Apart from the traditional _S_e_c_o_n_d_s _s_i_n_c_e _e_p_o_c_h, RRDtool does also understand at-style time specification. The specification is called "at-style" after the Unix command _a_t(1) that has moderately complex ways to specify time to run your job at a certain date and time. The at-style specification consists of two parts: the TTIIMMEE RREEFFEERREENNCCEE specification and the TTIIMMEE OOFFFFSSEETT specification. TTIIMMEE RREEFFEERREENNCCEE SSPPEECCIIFFIICCAATTIIOONN The time reference specification is used, well, to establish a reference moment in time (to which the time offset is then applied to). When present, it should come first, when omitted, it defaults to nnooww. On its own part, time reference consists of a _t_i_m_e_-_o_f_-_d_a_y reference (which should come first, if present) and a _d_a_y reference. The _t_i_m_e_-_o_f_-_d_a_y can be specified as HHHH::MMMM, HHHH..MMMM, or just HHHH. You can suffix it with aamm or ppmm or use 24-hours clock. Some special times of day are understood as well, including mmiiddnniigghhtt (00:00), nnoooonn (12:00) and British tteeaattiimmee (16:00). The _d_a_y can be specified as _m_o_n_t_h_-_n_a_m_e _d_a_y_-_o_f_-_t_h_e_-_m_o_n_t_h and optional a 2- or 4-digit _y_e_a_r number (e.g. March 8 1999). Alternatively, you can use _d_a_y_-_o_f_-_w_e_e_k_-_n_a_m_e (e.g. Monday), or one of the words: yyeesstteerrddaayy, ttooddaayy, ttoommoorrrrooww. You can also specify the _d_a_y as a full date in several numerical formats, including MMMM//DDDD//[[YYYY]]YYYY, DDDD..MMMM..[[YYYY]]YYYY, or YYYYYYYYMMMMDDDD. _N_O_T_E_1: this is different from the original _a_t(1) behavior, where a single-number date is interpreted as MMDD[YY]YY. _N_O_T_E_2: if you specify the _d_a_y in this way, the _t_i_m_e_-_o_f_-_d_a_y is REQUIRED as well. Finally, you can use the words nnooww, ssttaarrtt, eenndd or eeppoocchh as your time reference. NNooww refers to the current moment (and is also the default time reference). SSttaarrtt (eenndd) can be used to specify a time relative to the start (end) time for those tools that use these categories (rrrrddffeettcchh, rrdgraph) and eeppoocchh indicates the *IX epoch (*IX timestamp 0 = 1970-01-01 00:00:00 UTC). eeppoocchh is useful to disambiguate between a timestamp value and some forms of abbreviated date/time specifications, because it allows to use time offset specifications using units, eg. eeppoocchh+19711205s unambiguously denotes timestamp 19711205 and not 1971-12-05 00:00:00 UTC. Month and day of the week names can be used in their naturally abbreviated form (e.g., Dec for December, Sun for Sunday, etc.). The words nnooww, ssttaarrtt, eenndd can be abbreviated as nn, ss, ee. TTIIMMEE OOFFFFSSEETT SSPPEECCIIFFIICCAATTIIOONN The time offset specification is used to add/subtract certain time intervals to/from the time reference moment. It consists of a _s_i_g_n (++ or --) and an _a_m_o_u_n_t. The following time units can be used to specify the _a_m_o_u_n_t: yyeeaarrss, mmoonntthhss, wweeeekkss, ddaayyss, hhoouurrss, mmiinnuutteess, or sseeccoonnddss. These units can be used in singular or plural form, and abbreviated naturally or to a single letter (e.g. +3days, -1wk, -3y). Several time units can be combined (e.g., -5mon1w2d) or concatenated (e.g., -5h45min = -5h-45min = -6h+15min = -7h+1h30m-15min, etc.) _N_O_T_E_3: If you specify time offset in days, weeks, months, or years, you will end with the time offset that may vary depending on your time reference, because all those time units have no single well defined time interval value (1 year contains either 365 or 366 days, 1 month is 28 to 31 days long, and even 1 day may be not equal to 24 hours twice a year, when DST-related clock adjustments take place). To cope with this, when you use days, weeks, months, or years as your time offset units your time reference date is adjusted accordingly without too much further effort to ensure anything about it (in the hope that _m_k_t_i_m_e(3) will take care of this later). This may lead to some surprising (or even invalid!) results, e.g. 'May 31 -1month' = 'Apr 31' (meaningless) = 'May 1' (after _m_k_t_i_m_e(3) normalization); in the EET timezone '3:30am Mar 29 1999 -1 day' yields '3:30am Mar 28 1999' (Sunday) which is an invalid time/date combination (because of 3am -> 4am DST forward clock adjustment, see the below example). In contrast, hours, minutes, and seconds are well defined time intervals, and these are guaranteed to always produce time offsets exactly as specified (e.g. for EET timezone, '8:00 Mar 27 1999 +2 days' = '8:00 Mar 29 1999', but since there is 1-hour DST forward clock adjustment that occurs around 3:00 Mar 28 1999, the actual time interval between 8:00 Mar 27 1999 and 8:00 Mar 29 1999 equals 47 hours; on the other hand, '8:00 Mar 27 1999 +48 hours' = '9:00 Mar 29 1999', as expected) _N_O_T_E_4: The single-letter abbreviation for both mmoonntthhss and mmiinnuutteess is mm. To disambiguate them, the parser tries to read your mind :) by applying the following two heuristics: 1. If mm is used in context of (i.e. right after the) years, months, weeks, or days it is assumed to mean mmoonntthhss, while in the context of hours, minutes, and seconds it means minutes. (e.g., in -1y6m or +3w1m mm is interpreted as mmoonntthhss, while in -3h20m or +5s2m mm the parser decides for mmiinnuutteess). 2. Out of context (i.e. right after the ++ or -- sign) the meaning of mm is guessed from the number it directly follows. Currently, if the number's absolute value is below 25 it is assumed that mm means mmoonntthhss, otherwise it is treated as mmiinnuutteess. (e.g., -25m == -25 minutes, while +24m == +24 months) _F_i_n_a_l _N_O_T_E_S: Time specification is case-insensitive. Whitespace can be inserted freely or omitted altogether. There are, however, cases when whitespace is required (e.g., 'midnight Thu'). In this case you should either quote the whole phrase to prevent it from being taken apart by your shell or use '_' (underscore) or ',' (comma) which also count as whitespace (e.g., midnight_Thu or midnight,Thu). TTIIMMEE SSPPEECCIIFFIICCAATTIIOONN EEXXAAMMPPLLEESS _O_c_t _1_2 -- October 12 this year _-_1_m_o_n_t_h or _-_1_m -- current time of day, only a month before (may yield surprises, see NOTE3 above). _n_o_o_n _y_e_s_t_e_r_d_a_y _-_3_h_o_u_r_s -- yesterday morning; can also be specified as _9_a_m_-_1_d_a_y. _2_3_:_5_9 _3_1_._1_2_._1_9_9_9 -- 1 minute to the year 2000. _1_2_/_3_1_/_9_9 _1_1_:_5_9_p_m -- 1 minute to the year 2000 for imperialists. _1_2_a_m _0_1_/_0_1_/_0_1 -- start of the new millennium _e_n_d_-_3_w_e_e_k_s or _e_-_3_w -- 3 weeks before end time (may be used as start time specification). _s_t_a_r_t_+_6_h_o_u_r_s or _s_+_6_h -- 6 hours after start time (may be used as end time specification). _9_3_1_2_2_5_5_3_7 -- 18:45 July 5th, 1999 (yes, seconds since 1970 are valid as well). _1_9_9_7_0_7_0_3 _1_2_:_4_5 -- 12:45 July 3th, 1997 (my favorite, and its even got an ISO number (8601)). EENNVVIIRROONNMMEENNTT VVAARRIIAABBLLEESS The following environment variables may be used to change the behavior of "rrdtool fetch": RRRRDDCCAACCHHEEDD__AADDDDRREESSSS If this environment variable is set it will have the same effect as specifying the "--daemon" option on the command line. If both are present, the command line argument takes precedence. AAUUTTHHOORR Tobias Oetiker 1.4.7 2010-11-11 RRDFETCH(1) rrdtool-1.4.7/doc/cdeftutorial.txt0000644000175300017510000010775711707501551014153 00000000000000CDEFTUTORIAL(1) rrdtool CDEFTUTORIAL(1) NNAAMMEE cdeftutorial - Alex van den Bogaerdt's CDEF tutorial DDEESSCCRRIIPPTTIIOONN Intention of this document: to provide some examples of the commonly used parts of RRDtool's CDEF language. If you think some important feature is not explained properly, and if adding it to this document would benefit most users, please do ask me to add it. I will then try to provide an answer in the next release of this tutorial. No feedback equals no changes! Additions to this document are also welcome. -- Alex van den Bogaerdt WWhhyy tthhiiss ttuuttoorriiaall?? One of the powerful parts of RRDtool is its ability to do all sorts of calculations on the data retrieved from its 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. MMoorree rreeaaddiinngg 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. WWhhaatt aarree CCDDEEFFss?? 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. SSyynnttaaxx 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,* This tells RRDtool 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 the variable name used in the CDEF (inbits) must not be the same as the variable named in the DEF (inbytes)! RRPPNN--eexxpprreessssiioonnss 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. || 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. CCoonnvveerrttiinngg yyoouurr wwiisshheess ttoo RRPPNN 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 mathematical 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: 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 evaluated 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,+". This is called the commutative law of addition, but you may forget this right away, as long as you remember what it means. 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 must 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. SSoommee ssppeecciiaall nnuummbbeerrss TThhee uunnkknnoowwnn vvaalluuee 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(s) 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! Suppose the counter increments with one per second and you retrieve it every minute: counter value resulting rate 10'000 10'060 1; (10'060-10'000)/60 == 1 10'120 1; (10'120-10'060)/60 == 1 unknown unknown; you don't know the last value 10'240 unknown; you don't know the previous value 10'300 1; (10'300-10'240)/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. WWoorrkkiinngg wwiitthh uunnkknnoowwnn ddaattaa iinn yyoouurr ddaattaabbaassee 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 below that make this change. IInnffiinniittyy Infinite data is another form of a special number. It cannot be graphed because by definition you would never reach the infinite value. You can think of positive and negative infinity 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. WWoorrkkiinngg wwiitthh uunnkknnoowwnn ddaattaa aanndd iinnffiinniittyy 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. SSoommee eexxaammpplleess EExxaammppllee:: uussiinngg aa rreecceennttllyy ccrreeaatteedd RRRRDD 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: · 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. · Alternatively, 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 manually. Implementing the second method is described next: What we want is: "if the value is unknown, replace it with zero". This could be written 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 Rader's RPN guide if you have difficulties with the way I explained this last example. If you want to check this RPN expression, just mimic RRDtool 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. EExxaammppllee:: bbeetttteerr hhaannddlliinngg ooff uunnkknnoowwnn ddaattaa,, bbyy uussiinngg ttiimmee The 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: · If there is unknown data, look at the time that this sample was taken. · If the unknown value is before time xxx, make it zero. · 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, 1999, 00:35:57 MET DST. Translate this time in seconds since 1970-01-01 and it becomes 937'521'357. 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, 1999, 00:35:57 MET DST into 937'521'357 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 937'521'357, 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" 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 thoroughly 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. EExxaammppllee:: PPrreetteennddiinngg wweeiirrdd ddaattaa iissnn''tt tthheerree 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. 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 1'000 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 EExxaammppllee:: wwoorrkkiinngg oonn aa cceerrttaaiinn ttiimmee ssppaann 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 logical AND and logical 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,GT,TIME,endtime,LE,*,ds0,UNKN,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. EExxaammppllee:: YYoouu ssuussppeecctt ttoo hhaavvee pprroobblleemmss aanndd wwaanntt ttoo sseeee uunnkknnoowwnn ddaattaa.. 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 SSaammee eexxaammppllee uusseeffuull wwiitthh SSTTAACCKKeedd ddaattaa:: 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 writing two CDEFS: · It improves the readability of the script. · 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" TThhee eexxaammpplleess ffrroomm tthhee RRRRDD ggrraapphh mmaannuuaall ppaaggee DDeeggrreeeess CCeellssiiuuss vvss.. DDeeggrreeeess FFaahhrreennhheeiitt To convert Celsius into Fahrenheit use the formula F=9/5*C+32 rrdtool graph demo.png --title="Demo Graph" \ DEF:cel=demo.rrd:exhaust:AVERAGE \ CDEF:far=9,5,/,cel,*,32,+ \ 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=9,5,/,cel,*,32,+ 1. push 9, push 5 2. push function "divide" and process it the stack now contains 9/5 3. push variable "cel" 4. push function "multiply" and process it the stack now contains 9/5*cel 5. push 32 6. push function "plus" and process it the stack contains now the temperature in Fahrenheit CChhaannggiinngg uunnkknnoowwnn iinnttoo zzeerroo rrdtool graph demo.png --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. IInnffiinniittyy ddeemmoo 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 effect 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 7'200 (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 earlier 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,*" FFiilltteerriinngg ddaattaa 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=var,POP,TIME CDEF:prevtime=PREV(time) CDEF:derivate=var,prev1,-,time,prevtime,-,/ LINE3:derivate#000077:derivate LINE1:var#007700:'raw data' OOuutt ooff iiddeeaass ffoorr nnooww This document was created from questions asked by either myself or by other people on the RRDtool mailing 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: NNoo ffeeeeddbbaacckk eeqquuaallss nnoo cchhaannggeess!! SSEEEE AALLSSOO The RRDtool manpages AAUUTTHHOORR Alex van den Bogaerdt 1.4.7 2010-05-10 CDEFTUTORIAL(1) rrdtool-1.4.7/doc/rrdupdate.txt0000644000175300017510000002047111707501551013443 00000000000000RRDUPDATE(1) rrdtool RRDUPDATE(1) NNAAMMEE rrdupdate - Store a new set of values into the RRD SSYYNNOOPPSSIISS rrrrddttooooll {uuppddaattee | uuppddaatteevv} _f_i_l_e_n_a_m_e [----tteemmppllaattee|--tt _d_s_-_n_a_m_e[::_d_s_- _n_a_m_e]...] [----ddaaeemmoonn _a_d_d_r_e_s_s] [----] NN|_t_i_m_e_s_t_a_m_p::_v_a_l_u_e[::_v_a_l_u_e...] _a_t_- _t_i_m_e_s_t_a_m_p@@_v_a_l_u_e[::_v_a_l_u_e...] [_t_i_m_e_s_t_a_m_p::_v_a_l_u_e[::_v_a_l_u_e...] ...] DDEESSCCRRIIPPTTIIOONN The uuppddaattee function feeds new data values into an RRRRDD. The data is time aligned (interpolated) according to the properties of the RRRRDD to which the data is written. uuppddaatteevv This alternate version of uuppddaattee takes the same arguments and performs the same function. The _v stands for _v_e_r_b_o_s_e, which describes the output returned. uuppddaatteevv returns a list of any and all consolidated data points (CDPs) written to disk as a result of the invocation of update. The values are indexed by timestamp (time_t), RRA (consolidation function and PDPs per CDP), and data source (name). Note that depending on the arguments of the current and previous call to update, the list may have no entries or a large number of entries. Since uuppddaatteevv requires direct disk access, the ----ddaaeemmoonn option cannot be used with this command. _f_i_l_e_n_a_m_e The name of the RRRRDD you want to update. ----tteemmppllaattee|--tt _d_s_-_n_a_m_e[::_d_s_-_n_a_m_e]... By default, the uuppddaattee function expects its data input in the order the data sources are defined in the RRD, excluding any COMPUTE data sources (i.e. if the third data source DDSSTT is COMPUTE, the third input value will be mapped to the fourth data source in the RRRRDD and so on). This is not very error resistant, as you might be sending the wrong data into an RRD. The template switch allows you to specify which data sources you are going to update and in which order. If the data sources specified in the template are not available in the RRD file, the update process will abort with an error message. While it appears possible with the template switch to update data sources asynchronously, RRRRDDttooooll implicitly assigns non- COMPUTE data sources missing from the template the _*_U_N_K_N_O_W_N_* value. Do not specify a value for a COMPUTE DDSSTT in the uuppddaattee function. If this is done accidentally (and this can only be done using the template switch), RRRRDDttooooll will ignore the value specified for the COMPUTE DDSSTT. ----ddaaeemmoonn _a_d_d_r_e_s_s If given, RRRRDDTTooooll will try to connect to the caching daemon rrdcached at _a_d_d_r_e_s_s and will fail if the connection cannot be established. If the connection is successfully established the values will be sent to the daemon instead of accessing the files directly. For a list of accepted formats, see the --ll option in the rrdcached manual. NN|_t_i_m_e_s_t_a_m_p::_v_a_l_u_e[::_v_a_l_u_e...] The data used for updating the RRD was acquired at a certain time. This time can either be defined in seconds since 1970-01-01 or by using the letter 'N', in which case the update time is set to be the current time. Negative time values are subtracted from the current time. An AT_STYLE TIME SPECIFICATION (see the _r_r_d_f_e_t_c_h documentation) may also be used by delimiting the end of the time specification with the '@' character instead of a ':'. Getting the timing right to the second is especially important when you are working with data- sources of type CCOOUUNNTTEERR, DDEERRIIVVEE or AABBSSOOLLUUTTEE. When using negative time values, options and data have to be separated by two dashes (----), else the time value would be parsed as an option. See below for an example. When using negative time values, options and data have to be separated by two dashes (----), else the time value would be parsed as an option. See below for an example. The remaining elements of the argument are DS updates. The order of this list is the same as the order the data sources were defined in the RRA. If there is no data for a certain data-source, the letter UU (e.g., N:0.1:U:1) can be specified. The format of the value acquired from the data source is dependent on the data source type chosen. Normally it will be numeric, but the data acquisition modules may impose their very own parsing of this parameter as long as the colon (::) remains the data source value separator. EENNVVIIRROONNMMEENNTT VVAARRIIAABBLLEESS The following environment variables may be used to change the behavior of "rrdtool update": RRRRDDCCAACCHHEEDD__AADDDDRREESSSS If this environment variable is set it will have the same effect as specifying the "--daemon" option on the command line. If both are present, the command line argument takes precedence. EEXXAAMMPPLLEESS · "rrdtool update demo1.rrd N:3.44:3.15:U:23" Update the database file demo1.rrd with 3 known and one _*_U_N_K_N_O_W_N_* value. Use the current time as the update time. · "rrdtool update demo2.rrd 887457267:U 887457521:22 887457903:2.7" Update the database file demo2.rrd which expects data from a single data-source, three times. First with an _*_U_N_K_N_O_W_N_* value then with two regular readings. The update interval seems to be around 300 seconds. · "rrdtool update demo3.rrd -- -5:21 N:42" Update the database file demo3.rrd two times, using five seconds in the past and the current time as the update times. · "rrdtool update --cache /var/lib/rrd/demo3.rrd N:42" Update the file "/var/lib/rrd/demo3.rrd" with a single data source, using the current time. If the caching daemon cannot be reached, do nnoott fall back to direct file access. · "rrdtool update --daemon unix:/tmp/rrdd.sock demo4.rrd N:23" Use the UNIX domain socket "/tmp/rrdd.sock" to contact the caching daemon. If the caching daemon is not available, update the file "demo4.rrd" directly. WWAARRNNIINNGG:: Since a relative path is specified, the following disturbing effect may occur: If the daemon is available, the file relative to the working directory ooff tthhee ddaaeemmoonn is used. If the daemon is not available, the file relative to the current working directory of the invoking process is used. TThhiiss mmaayy uuppddaattee ttwwoo ddiiffffeerreenntt ffiilleess ddeeppeennddiinngg oonn wwhheetthheerr tthhee ddaaeemmoonn ccoouulldd bbee rreeaacchheedd oorr nnoott.. Don't do relative paths, kids! AAUUTTHHOORRSS Tobias Oetiker , Florian Forster 1.4.7 2009-06-02 RRDUPDATE(1) rrdtool-1.4.7/doc/rrdrestore.txt0000644000175300017510000000231211707501551013636 00000000000000RRDRESTORE(1) rrdtool RRDRESTORE(1) NNAAMMEE rrdrestore - Restore the contents of an RRD from its XML dump format SSYYNNOOPPSSIISS rrrrddttooooll rreessttoorree _f_i_l_e_n_a_m_e_._x_m_l _f_i_l_e_n_a_m_e_._r_r_d [----rraannggee--cchheecckk|--rr] DDEESSCCRRIIPPTTIIOONN The rreessttoorree function reads the XML representation of an RRD and converts it to the native RRRRDD format. _f_i_l_e_n_a_m_e_._x_m_l The name of the XXMMLL file you want to restore. _f_i_l_e_n_a_m_e_._r_r_d The name of the RRRRDD to restore. ----rraannggee--cchheecckk|--rr Make sure the values in the RRAs do not exceed the limits defined for the various data sources. ----ffoorrccee--oovveerrwwrriittee|--ff Allows RRRRDDttooooll to overwrite the destination RRRRDD. AAUUTTHHOORR Tobias Oetiker 1.4.7 2008-03-15 RRDRESTORE(1) rrdtool-1.4.7/doc/rrdgraph_examples.txt0000644000175300017510000002044511707501551015161 00000000000000RRDGRAPH_EXAMPLES(1) rrdtool RRDGRAPH_EXAMPLES(1) NNAAMMEE rrdgraph_examples - Examples for rrdtool graph SSYYNNOOPPSSIISS rrrrddttooooll ggrraapphh //hhoommee//hhttttppdd//hhttmmll//tteesstt..ppnngg ----iimmgg--ffoorrmmaatt PPNNGG followed by any of the examples below DDEESSCCRRIIPPTTIIOONN For your convenience some of the commands are explained here by using detailed examples. They are not always cut-and-paste ready because comments are intermixed with the examples. EEXXAAMMPPLLEESS DDaattaa wwiitthh mmuullttiippllee rreessoolluuttiioonnss --end now --start end-120000s --width 400 DEF:ds0a=/home/rrdtool/data/router1.rrd:ds0:AVERAGE DEF:ds0b=/home/rrdtool/data/router1.rrd:ds0:AVERAGE:step=1800 DEF:ds0c=/home/rrdtool/data/router1.rrd:ds0:AVERAGE:step=7200 LINE1:ds0a#0000FF:"default resolution\l" LINE1:ds0b#00CCFF:"resolution 1800 seconds per interval\l" LINE1:ds0c#FF00FF:"resolution 7200 seconds per interval\l" NNiicceellyy ffoorrmmaatttteedd lleeggeenndd sseeccttiioonn DEF:ds0=/home/rrdtool/data/router1.rrd:ds0:AVERAGE DEF:ds1=/home/rrdtool/data/router1.rrd:ds1:AVERAGE VDEF:ds0max=ds0,MAXIMUM VDEF:ds0avg=ds0,AVERAGE VDEF:ds0min=ds0,MINIMUM VDEF:ds0pct=ds0,95,PERCENT VDEF:ds1max=ds1,MAXIMUM VDEF:ds1avg=ds1,AVERAGE VDEF:ds1min=ds1,MINIMUM VDEF:ds1pct=ds1,95,PERCENT Note: consolidation occurs here. CDEF:ds0bits=ds0,8,* CDEF:ds1bits=ds1,8,* Note: 10 spaces to move text to the right COMMENT:" " Note: the column titles have to be as wide as the columns COMMENT:"Maximum " COMMENT:"Average " COMMENT:"Minimum " COMMENT:"95th percentile\l" AREA:ds0bits#00C000:"Inbound " GPRINT:ds0max:"%6.2lf %Sbps" GPRINT:ds0avg:"%6.2lf %Sbps" GPRINT:ds0min:"%6.2lf %Sbps" GPRINT:ds0pct:"%6.2lf %Sbps\l" LINE1:ds1bits#0000FF:"Outbound" GPRINT:ds1max:"%6.2lf %Sbps" GPRINT:ds1avg:"%6.2lf %Sbps" GPRINT:ds1min:"%6.2lf %Sbps" GPRINT:ds1pct:"%6.2lf %Sbps\l" OOffffsseettttiinngg aa lliinnee oonn tthhee yy--aaxxiiss Depending on your needs you can do this in two ways: · Offset the data, then graph this DEF:mydata=my.rrd:ds:AVERAGE Note: this will also influence any other command that uses "data" CDEF:data=mydata,100,+ LINE1:data#FF0000:"Data with offset" · Graph the original data, with an offset DEF:mydata=my.rrd:ds:AVERAGE Note: no color in the first line so it is not visible LINE1:100 Note: the second line gets stacked on top of the first one LINE1:data#FF0000:"Data with offset":STACK DDrraawwiinngg ddaasshheedd lliinneess Also works for HRULE and VRULE · default style: - - - - - LINE1:data#FF0000:"dashed line":dashes · more fancy style with offset: - - --- - --- - LINE1:data#FF0000:"another dashed line":dashes=15,5,5,10:dash-offset=10 TTiimmee rraannggeess Last four weeks: --start end-4w --end 00:00 January 2001: --start 20010101 --end start+31d January 2001: --start 20010101 --end 20010201 Last hour: --start end-1h Last 24 hours: Yesterday: --end 00:00 VViieewwiinngg tthhee ccuurrrreenntt aanndd pprreevviioouuss wweeeekk ttooggeetthheerr --end now --start end-1w DEF:thisweek=router.rrd:ds0:AVERAGE DEF:lastweek=router.rrd:ds0:AVERAGE:end=now-1w:start=end-1w Shift the data forward by one week (604800 seconds) SHIFT:lastweek:604800 [ more of the usual VDEF and CDEF stuff if you like ] AREA:lastweek#0000FF:Last\ week LINE1:thisweek#FF0000:This\ week AAbbeerrrraanntt BBeehhaavviioouurr DDeetteeccttiioonn If the specialized function RRRRAAss exist for aberrant behavior detection, they can be used to generate the graph of a time series with confidence bands and failures. rrdtool graph example.png \ DEF:obs=monitor.rrd:ifOutOctets:AVERAGE \ DEF:pred=monitor.rrd:ifOutOctets:HWPREDICT \ DEF:dev=monitor.rrd:ifOutOctets:DEVPREDICT \ DEF:fail=monitor.rrd:ifOutOctets:FAILURES \ TICK:fail#ffffa0:1.0:"Failures\: Average bits out" \ CDEF:scaledobs=obs,8,* \ CDEF:upper=pred,dev,2,*,+ \ CDEF:lower=pred,dev,2,*,- \ CDEF:scaledupper=upper,8,* \ CDEF:scaledlower=lower,8,* \ LINE2:scaledobs#0000ff:"Average bits out" \ LINE1:scaledupper#ff0000:"Upper Confidence Bound: Average bits out" \ LINE1:scaledlower#ff0000:"Lower Confidence Bound: Average bits out" This example generates a graph of the data series in blue (LINE2 with the scaledobs virtual data source), confidence bounds in red (scaledupper and scaledlower virtual data sources), and potential failures (i.e. potential aberrant aberrant behavior) marked by vertical yellow lines (the fail data source). The raw data comes from an AVERAGE RRRRAA, the finest resolution of the observed time series (one consolidated data point per primary data point). The predicted (or smoothed) values are stored in the HWPREDICT RRRRAA. The predicted deviations (think standard deviation) values are stored in the DEVPREDICT RRRRAA. Finally, the FAILURES RRRRAA contains indicators, with 1 denoting a potential failure. All of the data is rescaled to bits (instead of Octets) by multiplying by 8. The confidence bounds are computed by an offset of 2 deviations both above and below the predicted values (the CDEFs upper and lower). Vertical lines indicated potential failures are graphed via the TICK graph element, which converts non-zero values in an RRRRAA into tick marks. Here an axis-fraction argument of 1.0 means the tick marks span the entire y-axis, and hence become vertical lines on the graph. The choice of 2 deviations (a scaling factor) matches the default used internally by the FAILURES RRRRAA. If the internal value is changed (see rrdtune), this graphing command should be changed to be consistent. _A _n_o_t_e _o_n _d_a_t_a _r_e_d_u_c_t_i_o_n_: The rrrrddttooooll _g_r_a_p_h command is designed to plot data at a specified temporal resolution, regardless of the actually resolution of the data in the RRD file. This can present a problem for the specialized consolidation functions which maintain a one-to-one mapping between primary data points and consolidated data points. If a graph insists on viewing the contents of these RRRRAAss on a coarser temporal scale, the _g_r_a_p_h command tries to do something intelligent, but the confidence bands and failures no longer have the same meaning and may be misleading. SSEEEE AALLSSOO rrdgraph gives an overview of how rrrrddttooooll ggrraapphh works. rrdgraph_data describes DDEEFF,CCDDEEFF and VVDDEEFF in detail. rrdgraph_rpn describes the RRPPNN language used in the xxDDEEFF statements. rrdgraph_graph page describes all the graph and print functions. AAUUTTHHOORR Program by Tobias Oetiker This manual page by Alex van den Bogaerdt with corrections and/or additions by several people 1.4.7 2009-02-21 RRDGRAPH_EXAMPLES(1) rrdtool-1.4.7/doc/rrddump.txt0000644000175300017510000000622511707501551013127 00000000000000RRDDUMP(1) rrdtool RRDDUMP(1) NNAAMMEE rrddump - dump the contents of an RRD to XML format SSYYNNOOPPSSIISS rrrrddttooooll dduummpp _f_i_l_e_n_a_m_e_._r_r_d [_f_i_l_e_n_a_m_e_._x_m_l] [----hheeaaddeerr|--hh {none,xsd,dtd}] [----nnoo--hheeaaddeerr] [----ddaaeemmoonn _a_d_d_r_e_s_s] > _f_i_l_e_n_a_m_e_._x_m_l DDEESSCCRRIIPPTTIIOONN The dduummpp function writes the contents of an RRRRDD in human readable (?) XML format to a file or to stdout. This format can be read by rrdrestore. Together they allow you to transfer your files from one computer architecture to another as well to manipulate the contents of an RRRRDD file in a somewhat more convenient manner. _f_i_l_e_n_a_m_e_._r_r_d The name of the RRRRDD you want to dump. _f_i_l_e_n_a_m_e_._x_m_l The (optional) filename that you want to write the XML output to. If not specified, the XML will be printed to stdout. ----hheeaaddeerr|--hh {none,xsd,dtd} By default RRDtool will add a dtd header to the xml file. Here you can customize this to and xsd header or no header at all. ----nnoo--hheeaaddeerr A shortcut for --header=none. If you want to restore the dump with RRDtool 1.2 you should use the --no-header option since 1.2 can not deal with xml headers. ----ddaaeemmoonn _a_d_d_r_e_s_s Address of the rrdcached daemon. If specified, a "flush" command is sent to the server before reading the RRD files. This allows rrrrddttooooll to return fresh data even if the daemon is configured to cache values for a long time. For a list of accepted formats, see the --ll option in the rrdcached manual. rrdtool dump --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd EEXXAAMMPPLLEESS To transfer an RRD between architectures, follow these steps: 1. On the same system where the RRD was created, use rrrrddttooooll dduummpp to export the data to XML format. 2. Transfer the XML dump to the target system. 3. Run rrrrddttooooll rreessttoorree to create a new RRD from the XML dump. See rrrrddrreessttoorree for details. EENNVVIIRROONNMMEENNTT VVAARRIIAABBLLEESS The following environment variables may be used to change the behavior of "rrdtool dump": RRRRDDCCAACCHHEEDD__AADDDDRREESSSS If this environment variable is set it will have the same effect as specifying the "--daemon" option on the command line. If both are present, the command line argument takes precedence. AAUUTTHHOORR Tobias Oetiker 1.4.7 2009-10-14 RRDDUMP(1) rrdtool-1.4.7/doc/bin_dec_hex.txt0000644000175300017510000003630111707501551013677 00000000000000BIN_DEC_HEX(1) rrdtool BIN_DEC_HEX(1) NNAAMMEE bin_dec_hex - How to use binary, decimal, and hexadecimal notation. DDEESSCCRRIIPPTTIIOONN 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 to the left. 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 cryptic to you, this is what I've just 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 is incremented, it is reset to 0 and the position before (to the left) is incremented (from 0 to 1). 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". Leading zeros we don't write except if it is the only digit (number 0). And of course, we write zeros if they occur anywhere inside or at the end of a number: "00010" -> " 0010" -> " 010" -> " 10", but not " 1 ". This was pretty basic, you already knew this. Why did I tell it? Well, computers usually do not represent numbers with 10 different digits. They only use two different symbols, namely "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 as in the first list, we just used 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 explicitly specify the numbering system used, we implicitly use the decimal system. If we want to use any other numbering system, we'll have to make that clear. There are a few widely adopted methods to do so. One common form is to write 1010(2) which means that you wrote down a number in its binary representation. It is the number ten. If you would write 1010 without specifying the base, the number is interpreted as one thousand and ten using base 10. In books, another form is common. It uses subscripts (little characters, 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 by a little two just behind it. As the numbering system used is also called the base, we talk of the number 1100 base 2, the number 12 base 10. Within the binary system, it is common to write leading zeros. 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 65'535 (often in the decimal system a ' is used to separate blocks of three digits for readability) would be written down as 1111111111111111(2) which is 16 times the digit 1. This is difficult and prone to errors. Therefore, we usually would use another base, called hexadecimal. It uses 16 different symbols. First the symbols from the decimal system are used, thereafter we continue with 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 easily (and back). There is yet another system in use, called the octal system. This was more common in the old days, but is not used very often anymore. As you might find it in use sometimes, you should get used to it and we'll show it below. It's the same story as with the other representations, but with 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 practical 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: take four digits from the right and make a hexadecimal digit from it (see the table above). Repeat this until there are no more digits. And the 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 are specified with 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 their representation (base) 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, (no prefix, 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 only uses two symbols. Another example: %1010 would be 1 * 2^3 0 * 2^2 1 * 2^1 0 * 2^0 It would have been easier 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 you could use the next method. It will take some time to be able to do the estimates, but it will be easier when you use the system more frequently. We'll look at yet another way afterwards. First you need to know how many positions will be used in the other system. To do so, you need to know the maximum numbers you'll be using. Well, that's not as 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 as well: 16^4 = 0x10000 = 65536 16^3 = 0x1000 = 4096 16^2 = 0x100 = 256 16^1 = 0x10 = 16 If a number is smaller than 65'536 it will fit in four positions. If the number is bigger than 4'095, you must use position 4. How many times you can subtract 4'096 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. Let's try with 41'029. 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 41'029 ten times without going below zero. The left-most digit will therefore be "A", so we have 0xA????. The number is reduced to 41'029 - 10*4'096 = 41'029-40'960 = 69. 69 is smaller than 16^3 but not bigger than 16^2-1. The second digit is therefore "0" and we now have 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?. Subtract 64 from 69 (69 - 4*16) and the last digit is 5 --> 0xA045. The other method builds up the number from the right. Let's try 41'029 again. Divide by 16 and do not use fractions (only whole numbers). 41'029 / 16 is 2'564 with a remainder of 5. Write down 5. 2'564 / 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. 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 to one I am familiar with, then I will use the first method (like 32'770 which is into 32'768 + 2 and I 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 easily if you should write down a zero or a one: if you divide by two the remainder will be zero if it is an even number and one if it is 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 directly in the base that is used. In any representation it is the same, add the numbers on the right, write down the right-most digit from the result, remember the other digits and use them in the next round. Continue with the second digit 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, then check them with a calculator. The more you do the calculations yourself, the more you'll find that you didn't make mistakes. In the end, you'll do calculi in other bases as easily as you do them 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 short for Binary Calculator. It calculates not only in decimal, but in all bases you'll ever want to 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. AAUUTTHHOORR 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 1.4.7 2009-06-10 BIN_DEC_HEX(1) rrdtool-1.4.7/doc/librrd.30000644000175300017510000002205511707501550012251 00000000000000.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.07) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "librrd 3" .TH librrd 3 "2009-11-15" "1.4.7" "rrdtool" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" librrd \- RRD library functions .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBlibrrd\fR contains most of the functionality in \fBRRDTool\fR. The command line utilities and language bindings are often just wrappers around the code contained in \fBlibrrd\fR. .PP This manual page documents the \fBlibrrd\fR \s-1API\s0. .PP \&\fB\s-1NOTE:\s0\fR This document is a work in progress, and should be considered incomplete as long as this warning persists. For more information about the \fBlibrrd\fR functions, always consult the source code. .SH "CORE FUNCTIONS" .IX Header "CORE FUNCTIONS" .IP "\fBrrd_dump_cb_r(char *filename, int opt_header, rrd_output_callback_t cb, void *user)\fR" 4 .IX Item "rrd_dump_cb_r(char *filename, int opt_header, rrd_output_callback_t cb, void *user)" In some situations it is necessary to get the output of \f(CW\*(C`rrd_dump\*(C'\fR without writing it to a file or the standard output. In such cases an application can ask \fBrrd_dump_cb_r\fR to call an user-defined function each time there is output to be stored somewhere. This can be used, to e.g. directly feed an \s-1XML\s0 parser with the dumped output or transfer the resulting string in memory. .Sp The arguments for \fBrrd_dump_cb_r\fR are the same as for \fBrrd_dump_opt_r\fR except that the output filename parameter is replaced by the user-defined callback function and an additional parameter for the callback function that is passed untouched, i.e. to store information about the callback state needed for the user-defined callback to function properly. .Sp Recent versions of \fBrrd_dump_opt_r\fR internally use this callback mechanism to write their output to the file provided by the user. .Sp .Vb 7 \& size_t rrd_dump_opt_cb_fileout( \& const void *data, \& size_t len, \& void *user) \& { \& return fwrite(data, 1, len, (FILE *)user); \& } .Ve .Sp The associated call for \fBrrd_dump_cb_r\fR looks like .Sp .Vb 2 \& res = rrd_dump_cb_r(filename, opt_header, \& rrd_dump_opt_cb_fileout, (void *)out_file); .Ve .Sp where the last parameter specifies the file handle \fBrrd_dump_opt_cb_fileout\fR should write to. There's no specific condition for the callback to detect when it is called for the first time, nor for the last time. If you require this for initialization and cleanup you should do those tasks before and after calling \fBrrd_dump_cr_r\fR respectively. .SH "UTILITY FUNCTIONS" .IX Header "UTILITY FUNCTIONS" .IP "\fB\f(BIrrd_random()\fB\fR" 4 .IX Item "rrd_random()" Generates random numbers just like \fIrandom()\fR. This further ensures that the random number generator is seeded exactly once per process. .IP "\fBrrd_add_ptr(void ***dest, size_t *dest_size, void *src)\fR" 4 .IX Item "rrd_add_ptr(void ***dest, size_t *dest_size, void *src)" Dynamically resize the array pointed to by \f(CW\*(C`dest\*(C'\fR. \f(CW\*(C`dest_size\*(C'\fR is a pointer to the current size of \f(CW\*(C`dest\*(C'\fR. Upon successful \fIrealloc()\fR, the \&\f(CW\*(C`dest_size\*(C'\fR is incremented by 1 and the \f(CW\*(C`src\*(C'\fR pointer is stored at the end of the new \f(CW\*(C`dest\*(C'\fR. Returns 1 on success, 0 on failure. .Sp .Vb 5 \& type **arr = NULL; \& type *elem = "whatever"; \& size_t arr_size = 0; \& if (!rrd_add_ptr(&arr, &arr_size, elem)) \& handle_failure(); .Ve .IP "\fBrrd_add_strdup(char ***dest, size_t *dest_size, char *src)\fR" 4 .IX Item "rrd_add_strdup(char ***dest, size_t *dest_size, char *src)" Like \f(CW\*(C`rrd_add_ptr\*(C'\fR, except adds a \f(CW\*(C`strdup\*(C'\fR of the source string. .Sp .Vb 5 \& char **arr = NULL; \& size_t arr_size = NULL; \& char *str = "example text"; \& if (!rrd_add_strdup(&arr, &arr_size, str)) \& handle_failure(); .Ve .IP "\fBrrd_free_ptrs(void ***src, size_t *cnt)\fR" 4 .IX Item "rrd_free_ptrs(void ***src, size_t *cnt)" Free an array of pointers allocated by \f(CW\*(C`rrd_add_ptr\*(C'\fR or \&\f(CW\*(C`rrd_add_strdup\*(C'\fR. Also frees the array pointer itself. On return, the source pointer will be \s-1NULL\s0 and the count will be zero. .Sp .Vb 3 \& /* created as above */ \& rrd_free_ptrs(&arr, &arr_size); \& /* here, arr == NULL && arr_size == 0 */ .Ve .IP "\fBrrd_mkdir_p(const char *pathname, mode_t mode)\fR" 4 .IX Item "rrd_mkdir_p(const char *pathname, mode_t mode)" Create the directory named \f(CW\*(C`pathname\*(C'\fR including all of its parent directories (similar to \f(CW\*(C`mkdir \-p\*(C'\fR on the command line \- see \fImkdir\fR\|(1) for more information). The argument \f(CW\*(C`mode\*(C'\fR specifies the permissions to use. It is modified by the process's \f(CW\*(C`umask\*(C'\fR. See \fImkdir\fR\|(2) for more details. .Sp The function returns 0 on success, a negative value else. In case of an error, \&\f(CW\*(C`errno\*(C'\fR is set accordingly. Aside from the errors documented in \fImkdir\fR\|(2), the function may fail with the following errors: .RS 4 .IP "\fB\s-1EINVAL\s0\fR" 4 .IX Item "EINVAL" \&\f(CW\*(C`pathname\*(C'\fR is \f(CW\*(C`NULL\*(C'\fR or the empty string. .IP "\fB\s-1ENOMEM\s0\fR" 4 .IX Item "ENOMEM" Insufficient memory was available. .IP "\fBany error returned by \f(BIstat\fB\|(2)\fR" 4 .IX Item "any error returned by stat" .RE .RS 4 .Sp In contrast to \fImkdir\fR\|(2), the function does \fBnot\fR fail if \f(CW\*(C`pathname\*(C'\fR already exists and is a directory. .RE .SH "AUTHOR" .IX Header "AUTHOR" \&\s-1RRD\s0 Contributors rrdtool-1.4.7/doc/rrdtutorial.10000644000175300017510000016400011707501550013341 00000000000000.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.07) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "RRDTUTORIAL 1" .TH RRDTUTORIAL 1 "2011-01-15" "1.4.7" "rrdtool" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" rrdtutorial \- Alex van den Bogaerdt's RRDtool tutorial .SH "DESCRIPTION" .IX Header "DESCRIPTION" RRDtool is written by Tobias Oetiker with contributions from many people all around the world. This document is written by Alex van den Bogaerdt to help you understand what RRDtool is and what it can do for you. .PP The documentation provided with RRDtool can be too technical for some people. This tutorial is here to help you understand the basics of RRDtool. It should prepare you to read the documentation yourself. It also explains the general things about statistics with a focus on networking. .SH "TUTORIAL" .IX Header "TUTORIAL" .SS "Important" .IX Subsection "Important" Please don't skip ahead in this document! The first part of this document explains the basics and may be boring. But if you don't understand the basics, the examples will not be as meaningful to you. .PP Sometimes things change. This example used to provide numbers like \&\*(L"0.04\*(R" in stead of \*(L"4.00000e\-02\*(R". Those are really the same numbers, just written down differently. Don't be alarmed if a future version of rrdtool displays a slightly different form of output. The examples in this document are correct for version 1.2.0 of RRDtool. .PP Also, sometimes bugs do occur. They may also influence the outcome of the examples. Example speed4.png was suffering from this (the handling of unknown data in an if-statement was wrong). Normal data will be just fine (a bug in rrdtool wouldn't last long) but special cases like NaN, \s-1INF\s0 and so on may last a bit longer. Try another version if you can, or just live with it. .PP I fixed the speed4.png example (and added a note). There may be other examples which suffer from the same or a similar bug. Try to fix it yourself, which is a great excercise. But please do not submit your result as a fix to the source of this document. Discuss it on the user's list, or write to me. .SS "What is RRDtool?" .IX Subsection "What is RRDtool?" RRDtool refers to Round Robin Database tool. Round robin is a technique that works with a fixed amount of data, and a pointer to the current element. Think of a circle with some dots plotted on the edge. These dots are the places where data can be stored. Draw an arrow from the center of the circle to one of the dots; this is the pointer. When the current data is read or written, the pointer moves to the next element. As we are on a circle there is neither a beginning nor an end, you can go on and on and on. After a while, all the available places will be used and the process automatically reuses old locations. This way, the dataset will not grow in size and therefore requires no maintenance. RRDtool works with Round Robin Databases (RRDs). It stores and retrieves data from them. .SS "What data can be put into an \s-1RRD\s0?" .IX Subsection "What data can be put into an RRD?" You name it, it will probably fit as long as it is some sort of time-series data. This means you have to be able to measure some value at several points in time and provide this information to RRDtool. If you can do this, RRDtool will be able to store it. The values must be numerical but don't have to be integers, as is the case with \s-1MRTG\s0 (the next section will give more details on this more specialized application). .PP Many examples below talk about \s-1SNMP\s0 which is an acronym for Simple Network Management Protocol. \*(L"Simple\*(R" refers to the protocol. It does not mean it is simple to manage or monitor a network. After working your way through this document, you should know enough to be able to understand what people are talking about. For now, just realize that \&\s-1SNMP\s0 can be used to query devices for the values of counters they keep. It is the value from those counters that we want to store in the \s-1RRD\s0. .SS "What can I do with this tool?" .IX Subsection "What can I do with this tool?" RRDtool originated from \s-1MRTG\s0 (Multi Router Traffic Grapher). \s-1MRTG\s0 started as a tiny little script for graphing the use of a university's connection to the Internet. \s-1MRTG\s0 was later (ab\-)used as a tool for graphing other data sources including temperature, speed, voltage, number of printouts and the like. .PP Most likely you will start to use RRDtool to store and process data collected via \s-1SNMP\s0. The data will most likely be bytes (or bits) transferred from and to a network or a computer. But it can also be used to display tidal waves, solar radiation, power consumption, number of visitors at an exhibition, noise levels near an airport, temperature on your favorite holiday location, temperature in the fridge and whatever your imagination can come up with. .PP You only need a sensor to measure the data and be able to feed the numbers into RRDtool. RRDtool then lets you create a database, store data in it, retrieve that data and create graphs in \s-1PNG\s0 format for display on a web browser. Those \s-1PNG\s0 images are dependent on the data you collected and could be, for instance, an overview of the average network usage, or the peaks that occurred. .SS "What if I still have problems after reading this document?" .IX Subsection "What if I still have problems after reading this document?" First of all: read it again! You may have missed something. If you are unable to compile the sources and you have a fairly common \&\s-1OS\s0, it will probably not be the fault of RRDtool. There may be pre-compiled versions around on the Internet. If they come from trusted sources, get one of those. .PP If on the other hand the program works but does not give you the expected results, it will be a problem with configuring it. Review your configuration and compare it with the examples that follow. .PP There is a mailing list and an archive of it. Read the list for a few weeks and search the archive. It is considered rude to just ask a question without searching the archives: your problem may already have been solved for somebody else! This is true for most, if not all, mailing lists and not only for this particular one. Look in the documentation that came with RRDtool for the location and usage of the list. .PP I suggest you take a moment to subscribe to the mailing list right now by sending an email to with a subject of \*(L"subscribe\*(R". If you ever want to leave this list, just write an email to the same address but now with a subject of \*(L"unsubscribe\*(R". .SS "How will you help me?" .IX Subsection "How will you help me?" By giving you some detailed descriptions with detailed examples. I assume that following the instructions in the order presented will give you enough knowledge of RRDtool to experiment for yourself. If it doesn't work the first time, don't give up. Reread the stuff that you did understand, you may have missed something. .PP By following the examples you get some hands-on experience and, even more important, some background information of how it works. .PP You will need to know something about hexadecimal numbers. If you don't then start with reading bin_dec_hex before you continue here. .SS "Your first Round Robin Database" .IX Subsection "Your first Round Robin Database" In my opinion the best way to learn something is to actually do it. Why not start right now? We will create a database, put some values in it and extract this data again. Your output should be the same as the output that is included in this document. .PP We will start with some easy stuff and compare a car with a router, or compare kilometers (miles if you wish) with bits and bytes. It's all the same: some number over some time. .PP Assume we have a device that transfers bytes to and from the Internet. This device keeps a counter that starts at zero when it is turned on, increasing with every byte that is transferred. This counter will probably have a maximum value. If this value is reached and an extra byte is counted, the counter starts over at zero. This is the same as many counters in the world such as the mileage counter in a car. .PP Most discussions about networking talk about bits per second so lets get used to that right away. Assume a byte is eight bits and start to think in bits not bytes. The counter, however, still counts bytes! In the \s-1SNMP\s0 world most of the counters are 32 bits. That means they are counting from 0 to 4294967295. We will use these values in the examples. The device, when asked, returns the current value of the counter. We know the time that has passes since we last asked so we now know how many bytes have been transferred ***on average*** per second. This is not very hard to calculate. First in words, then in calculations: .IP "1." 3 Take the current counter, subtract the previous value from it. .IP "2." 3 Do the same with the current time and the previous time (in seconds). .IP "3." 3 Divide the outcome of (1) by the outcome of (2), the result is the amount of bytes per second. Multiply by eight to get the number of bits per second (bps). .PP .Vb 1 \& bps = (counter_now \- counter_before) / (time_now \- time_before) * 8 .Ve .PP For some people it may help to translate this to an automobile example. Do not try this example, and if you do, don't blame me for the results! .PP People who are not used to think in kilometers per hour can translate most into miles per hour by dividing km by 1.6 (close enough). I will use the following abbreviations: .PP .Vb 6 \& m: meter \& km: kilometer (= 1000 meters). \& h: hour \& s: second \& km/h: kilometers per hour \& m/s: meters per second .Ve .PP You are driving a car. At 12:05 you read the counter in the dashboard and it tells you that the car has moved 12345 km until that moment. At 12:10 you look again, it reads 12357 km. This means you have traveled 12 km in five minutes. A scientist would translate that into meters per second and this makes a nice comparison toward the problem of (bytes per five minutes) versus (bits per second). .PP We traveled 12 kilometers which is 12000 meters. We did that in five minutes or 300 seconds. Our speed is 12000m / 300s or 40 m/s. .PP We could also calculate the speed in km/h: 12 times 5 minutes is an hour, so we have to multiply 12 km by 12 to get 144 km/h. For our native English speaking friends: that's 90 mph so don't try this example at home or where I live :) .PP Remember: these numbers are averages only. There is no way to figure out from the numbers, if you drove at a constant speed. There is an example later on in this tutorial that explains this. .PP I hope you understand that there is no difference in calculating m/s or bps; only the way we collect the data is different. Even the k from kilo is the same as in networking terms k also means 1000. .PP We will now create a database where we can keep all these interesting numbers. The method used to start the program may differ slightly from \&\s-1OS\s0 to \s-1OS\s0, but I assume you can figure it out if it works different on your's. Make sure you do not overwrite any file on your system when executing the following command and type the whole line as one long line (I had to split it for readability) and skip all of the '\e' characters. .PP .Vb 5 \& rrdtool create test.rrd \e \& \-\-start 920804400 \e \& DS:speed:COUNTER:600:U:U \e \& RRA:AVERAGE:0.5:1:24 \e \& RRA:AVERAGE:0.5:6:10 .Ve .PP (So enter: \f(CW\*(C`rrdtool create test.rrd \-\-start 920804400 DS ...\*(C'\fR) .SS "What has been created?" .IX Subsection "What has been created?" We created the round robin database called test (test.rrd) which starts at noon the day I started writing this document, 7th of March, 1999 (this date translates to 920804400 seconds as explained below). Our database holds one data source (\s-1DS\s0) named \*(L"speed\*(R" that represents a counter. This counter is read every five minutes (this is the default therefore you don't have to put \f(CW\*(C`\-\-step=300\*(C'\fR). In the same database two round robin archives (RRAs) are kept, one averages the data every time it is read (e.g., there's nothing to average) and keeps 24 samples (24 times 5 minutes is 2 hours). The other averages 6 values (half hour) and contains 10 such averages (e.g. 5 hours). .PP RRDtool works with special time stamps coming from the \s-1UNIX\s0 world. This time stamp is the number of seconds that passed since January 1st 1970 \s-1UTC\s0. The time stamp value is translated into local time and it will therefore look different for different time zones. .PP Chances are that you are not in the same part of the world as I am. This means your time zone is different. In all examples where I talk about time, the hours may be wrong for you. This has little effect on the results of the examples, just correct the hours while reading. As an example: where I will see \*(L"12:05\*(R" the \s-1UK\s0 folks will see \*(L"11:05\*(R". .PP We now have to fill our database with some numbers. We'll pretend to have read the following numbers: .PP .Vb 10 \& 12:05 12345 km \& 12:10 12357 km \& 12:15 12363 km \& 12:20 12363 km \& 12:25 12363 km \& 12:30 12373 km \& 12:35 12383 km \& 12:40 12393 km \& 12:45 12399 km \& 12:50 12405 km \& 12:55 12411 km \& 13:00 12415 km \& 13:05 12420 km \& 13:10 12422 km \& 13:15 12423 km .Ve .PP We fill the database as follows: .PP .Vb 5 \& rrdtool update test.rrd 920804700:12345 920805000:12357 920805300:12363 \& rrdtool update test.rrd 920805600:12363 920805900:12363 920806200:12373 \& rrdtool update test.rrd 920806500:12383 920806800:12393 920807100:12399 \& rrdtool update test.rrd 920807400:12405 920807700:12411 920808000:12415 \& rrdtool update test.rrd 920808300:12420 920808600:12422 920808900:12423 .Ve .PP This reads: update our test database with the following numbers .PP .Vb 2 \& time 920804700, value 12345 \& time 920805000, value 12357 .Ve .PP etcetera. .PP As you can see, it is possible to feed more than one value into the database in one command. I had to stop at three for readability but the real maximum per line is \s-1OS\s0 dependent. .PP We can now retrieve the data from our database using \*(L"rrdtool fetch\*(R": .PP .Vb 1 \& rrdtool fetch test.rrd AVERAGE \-\-start 920804400 \-\-end 920809200 .Ve .PP It should return the following output: .PP .Vb 1 \& speed \& \& 920804700: nan \& 920805000: 4.0000000000e\-02 \& 920805300: 2.0000000000e\-02 \& 920805600: 0.0000000000e+00 \& 920805900: 0.0000000000e+00 \& 920806200: 3.3333333333e\-02 \& 920806500: 3.3333333333e\-02 \& 920806800: 3.3333333333e\-02 \& 920807100: 2.0000000000e\-02 \& 920807400: 2.0000000000e\-02 \& 920807700: 2.0000000000e\-02 \& 920808000: 1.3333333333e\-02 \& 920808300: 1.6666666667e\-02 \& 920808600: 6.6666666667e\-03 \& 920808900: 3.3333333333e\-03 \& 920809200: nan \& 920809500: nan .Ve .PP Note that you might get more rows than you expect. The reason for this is that you ask for a time range that ends on 920809200. The number that is written behind 920809200: in the list above covers the time range from 920808900 to 920809200, \s-1EXCLUDING\s0 920809200. Hence to be on the sure side, you receive the entry from 920809200 to 920809500 as well since it \s-1INCLUDES\s0 920809200. You may also see \*(L"NaN\*(R" instead of \*(L"nan\*(R" this is \s-1OS\s0 dependent. \&\*(L"NaN\*(R" stands for \*(L"Not A Number\*(R". If your \s-1OS\s0 writes \*(L"U\*(R" or \*(L"\s-1UNKN\s0\*(R" or something similar that's okay. If something else is wrong, it will probably be due to an error you made (assuming that my tutorial is correct of course :\-). In that case: delete the database and try again. .PP The meaning of the above output will become clear below. .SS "Time to create some graphics" .IX Subsection "Time to create some graphics" Try the following command: .PP .Vb 4 \& rrdtool graph speed.png \e \& \-\-start 920804400 \-\-end 920808000 \e \& DEF:myspeed=test.rrd:speed:AVERAGE \e \& LINE2:myspeed#FF0000 .Ve .PP This will create speed.png which starts at 12:00 and ends at 13:00. There is a definition of a variable called myspeed, using the data from \s-1RRA\s0 \&\*(L"speed\*(R" out of database \*(L"test.rrd\*(R". The line drawn is 2 pixels high and represents the variable myspeed. The color is red (specified by its rgb-representation, see below). .PP You'll notice that the start of the graph is not at 12:00 but at 12:05. This is because we have insufficient data to tell the average before that time. This will only happen when you miss some samples, this will not happen a lot, hopefully. .PP If this has worked: congratulations! If not, check what went wrong. .PP The colors are built up from red, green and blue. For each of the components, you specify how much to use in hexadecimal where 00 means not included and \s-1FF\s0 means fully included. The \*(L"color\*(R" white is a mixture of red, green and blue: \s-1FFFFFF\s0 The \*(L"color\*(R" black is all colors off: 000000 .PP .Vb 5 \& red #FF0000 \& green #00FF00 \& blue #0000FF \& magenta #FF00FF (mixed red with blue) \& gray #555555 (one third of all components) .Ve .PP Additionally you can (with a recent RRDtool) add an alpha channel (transparency). The default will be \*(L"\s-1FF\s0\*(R" which means non-transparent. .PP The \s-1PNG\s0 you just created can be displayed using your favorite image viewer. Web browsers will display the \s-1PNG\s0 via the \s-1URL\s0 \&\*(L"file:///the/path/to/speed.png\*(R" .SS "Graphics with some math" .IX Subsection "Graphics with some math" When looking at the image, you notice that the horizontal axis is labeled 12:10, 12:20, 12:30, 12:40 and 12:50. Sometimes a label doesn't fit (12:00 and 13:00 would be likely candidates) so they are skipped. .PP The vertical axis displays the range we entered. We provided kilometers and when divided by 300 seconds, we get very small numbers. To be exact, the first value was 12 (12357\-12345) and divided by 300 this makes 0.04, which is displayed by RRDtool as \*(L"40 m\*(R" meaning \*(L"40/1000\*(R". The \*(L"m\*(R" (milli) has nothing to do with meters (also m), kilometers or millimeters! RRDtool doesn't know about the physical units of our data, it just works with dimensionless numbers. .PP If we had measured our distances in meters, this would have been (12357000\-12345000)/300 = 12000/300 = 40. .PP As most people have a better feel for numbers in this range, we'll correct that. We could recreate our database and store the correct data, but there is a better way: we do some calculations while creating the png file! .PP .Vb 6 \& rrdtool graph speed2.png \e \& \-\-start 920804400 \-\-end 920808000 \e \& \-\-vertical\-label m/s \e \& DEF:myspeed=test.rrd:speed:AVERAGE \e \& CDEF:realspeed=myspeed,1000,\e* \e \& LINE2:realspeed#FF0000 .Ve .PP Note: I need to escape the multiplication operator * with a backslash. If I don't, the operating system may interpret it and use it for file name expansion. You could also place the line within quotation marks like so: .PP .Vb 1 \& "CDEF:realspeed=myspeed,1000,*" \e .Ve .PP It boils down to: it is RRDtool which should see *, not your shell. And it is your shell interpreting \e, not RRDtool. You may need to adjust examples accordingly if you happen to use an operating system or shell which behaves differently. .PP After viewing this \s-1PNG\s0, you notice the \*(L"m\*(R" (milli) has disappeared. This it what the correct result would be. Also, a label has been added to the image. Apart from the things mentioned above, the \s-1PNG\s0 should look the same. .PP The calculations are specified in the \s-1CDEF\s0 part above and are in Reverse Polish Notation (\*(L"\s-1RPN\s0\*(R"). What we requested RRDtool to do is: \&\*(L"take the data source myspeed and the number 1000; multiply those\*(R". Don't bother with \s-1RPN\s0 yet, it will be explained later on in more detail. Also, you may want to read my tutorial on CDEFs and Steve Rader's tutorial on \s-1RPN\s0. But first finish this tutorial. .PP Hang on! If we can multiply values with 1000, it should also be possible to display kilometers per hour from the same data! .PP To change a value that is measured in meters per second: .PP .Vb 3 \& Calculate meters per hour: value * 3600 \& Calculate kilometers per hour: value / 1000 \& Together this makes: value * (3600/1000) or value * 3.6 .Ve .PP In our example database we made a mistake and we need to compensate for this by multiplying with 1000. Applying that correction: .PP .Vb 1 \& value * 3.6 * 1000 == value * 3600 .Ve .PP Now let's create this \s-1PNG\s0, and add some more magic ... .PP .Vb 10 \& rrdtool graph speed3.png \e \& \-\-start 920804400 \-\-end 920808000 \e \& \-\-vertical\-label km/h \e \& DEF:myspeed=test.rrd:speed:AVERAGE \e \& "CDEF:kmh=myspeed,3600,*" \e \& CDEF:fast=kmh,100,GT,kmh,0,IF \e \& CDEF:good=kmh,100,GT,0,kmh,IF \e \& HRULE:100#0000FF:"Maximum allowed" \e \& AREA:good#00FF00:"Good speed" \e \& AREA:fast#FF0000:"Too fast" .Ve .PP Note: here we use another means to escape the * operator by enclosing the whole string in double quotes. .PP This graph looks much better. Speed is shown in km/h and there is even an extra line with the maximum allowed speed (on the road I travel on). I also changed the colors used to display speed and changed it from a line into an area. .PP The calculations are more complex now. For speed measurements within the speed limit they are: .PP .Vb 2 \& Check if kmh is greater than 100 ( kmh,100 ) GT \& If so, return 0, else kmh ((( kmh,100 ) GT ), 0, kmh) IF .Ve .PP For values above the speed limit: .PP .Vb 2 \& Check if kmh is greater than 100 ( kmh,100 ) GT \& If so, return kmh, else return 0 ((( kmh,100) GT ), kmh, 0) IF .Ve .SS "Graphics Magic" .IX Subsection "Graphics Magic" I like to believe there are virtually no limits to how RRDtool graph can manipulate data. I will not explain how it works, but look at the following \s-1PNG:\s0 .PP .Vb 10 \& rrdtool graph speed4.png \e \& \-\-start 920804400 \-\-end 920808000 \e \& \-\-vertical\-label km/h \e \& DEF:myspeed=test.rrd:speed:AVERAGE \e \& CDEF:nonans=myspeed,UN,0,myspeed,IF \e \& CDEF:kmh=nonans,3600,* \e \& CDEF:fast=kmh,100,GT,100,0,IF \e \& CDEF:over=kmh,100,GT,kmh,100,\-,0,IF \e \& CDEF:good=kmh,100,GT,0,kmh,IF \e \& HRULE:100#0000FF:"Maximum allowed" \e \& AREA:good#00FF00:"Good speed" \e \& AREA:fast#550000:"Too fast" \e \& STACK:over#FF0000:"Over speed" .Ve .PP Remember the note in the beginning? I had to remove unknown data from this example. The 'nonans' \s-1CDEF\s0 is new, and the 6th line (which used to be the 5th line) used to read 'CDEF:kmh=myspeed,3600,*' .PP Let's create a quick and dirty \s-1HTML\s0 page to view the three PNGs: .PP .Vb 7 \& Speed \& Speed in meters per second \&
\& Speed in kilometers per hour \&
\& Traveled too fast? \& .Ve .PP Name the file \*(L"speed.html\*(R" or similar, and look at it in your web browser. .PP Now, all you have to do is measure the values regularly and update the database. When you want to view the data, recreate the PNGs and make sure to refresh them in your browser. (Note: just clicking reload may not be enough, especially when proxies are involved. Try shift-reload or ctrl\-F5). .SS "Updates in Reality" .IX Subsection "Updates in Reality" We've already used the \f(CW\*(C`update\*(C'\fR command: it took one or more parameters in the form of \*(L"
\& / \& \& []: \& : .Ve .Sp If the \fB\-l\fR option is not specified the default address, \&\f(CW\*(C`unix:/tmp/rrdcached.sock\*(C'\fR, will be used. .IP "\fB\-s\fR \fIgroup_name\fR|\fIgid\fR" 4 .IX Item "-s group_name|gid" Set the group permissions of a \s-1UNIX\s0 domain socket. The option accepts either a numeric group id or group name. That group will then have both read and write permissions (the socket will have file permissions 0750) for the socket and, therefore, is able to send commands to the daemon. This may be useful in cases where you cannot easily run all \s-1RRD\s0 processes with the same user privileges (e.g. graph generating \s-1CGI\s0 scripts that typically run in the permission context of the web server). .Sp This option affects the \fIfollowing\fR \s-1UNIX\s0 socket addresses (the following \&\fB\-l\fR options) or the default socket (if no \fB\-l\fR options have been specified), i.e., you may specify different settings for different sockets. .Sp The default is not to change ownership or permissions of the socket and, thus, use the system default. .IP "\fB\-m\fR \fImode\fR" 4 .IX Item "-m mode" Set the file permissions of a \s-1UNIX\s0 domain socket. The option accepts an octal number representing the bit pattern for the mode (see \fIchmod\fR\|(1) for details). .Sp Please note that not all systems honor this setting. On Linux, read/write permissions are required to connect to a \s-1UNIX\s0 socket. However, many BSD-derived systems ignore permissions for \s-1UNIX\s0 sockets. See \fIunix\fR\|(7) for details. .Sp This option affects the \fIfollowing\fR \s-1UNIX\s0 socket addresses (the following \&\fB\-l\fR options) or the default socket (if no \fB\-l\fR options have been specified), i.e., you may specify different settings for different sockets. .Sp The default is not to change ownership or permissions of the socket and, thus, use the system default. .IP "\fB\-P\fR \fIcommand\fR[,\fIcommand\fR[,...]]" 4 .IX Item "-P command[,command[,...]]" Specifies the commands accepted via a network socket. This allows administrators of \fIRRDCacheD\fR to control the actions accepted from various sources. .Sp The arguments given to the \fB\-P\fR option is a comma separated list of commands. For example, to allow the \f(CW\*(C`FLUSH\*(C'\fR and \f(CW\*(C`PENDING\*(C'\fR commands one could specify: .Sp .Vb 1 \& rrdcached \-P FLUSH,PENDING $MORE_ARGUMENTS .Ve .Sp The \fB\-P\fR option affects the \fIfollowing\fR socket addresses (the following \fB\-l\fR options) or the default socket (if no \fB\-l\fR options have been specified). In the following example, only the IPv4 network socket (address \&\f(CW10.0.0.1\fR) will be restricted to the \f(CW\*(C`FLUSH\*(C'\fR and \f(CW\*(C`PENDING\*(C'\fR commands: .Sp .Vb 1 \& rrdcached \-l unix:/some/path \-P FLUSH,PENDING \-l 10.0.0.1 .Ve .Sp A complete list of available commands can be found in the section \&\*(L"Valid Commands\*(R" below. There are two minor special exceptions: .RS 4 .IP "\(bu" 4 The \f(CW\*(C`HELP\*(C'\fR and \f(CW\*(C`QUIT\*(C'\fR commands are always allowed. .IP "\(bu" 4 If the \f(CW\*(C`BATCH\*(C'\fR command is accepted, the \fB.\fR\ command will automatically be accepted, too. .RE .RS 4 .Sp Please also read \*(L"\s-1SECURITY\s0 \s-1CONSIDERATIONS\s0\*(R" below. .RE .IP "\fB\-w\fR \fItimeout\fR" 4 .IX Item "-w timeout" Data is written to disk every \fItimeout\fR seconds. If this option is not specified the default interval of 300\ seconds will be used. .IP "\fB\-z\fR \fIdelay\fR" 4 .IX Item "-z delay" If specified, rrdcached will delay writing of each \s-1RRD\s0 for a random number of seconds in the range\ [0,\fIdelay\fR). This will avoid too many writes being queued simultaneously. This value should be no greater than the value specified in \fB\-w\fR. By default, there is no delay. .IP "\fB\-f\fR \fItimeout\fR" 4 .IX Item "-f timeout" Every \fItimeout\fR seconds the entire cache is searched for old values which are written to disk. This only concerns files to which updates have stopped, so setting this to a high value, such as 3600\ seconds, is acceptable in most cases. This timeout defaults to 3600\ seconds. .IP "\fB\-p\fR \fIfile\fR" 4 .IX Item "-p file" Sets the name and location of the PID-file. If not specified, the default, \&\f(CW\*(C`\f(CI$localststedir\f(CW/run/rrdcached.pid\*(C'\fR will be used. .IP "\fB\-t\fR \fIwrite_threads\fR" 4 .IX Item "-t write_threads" Specifies the number of threads used for writing \s-1RRD\s0 files. The default is\ 4. Increasing this number will allow rrdcached to have more simultaneous I/O requests into the kernel. This may allow the kernel to re-order disk writes, resulting in better disk throughput. .IP "\fB\-j\fR \fIdir\fR" 4 .IX Item "-j dir" Write updates to a journal in \fIdir\fR. In the event of a program or system crash, this will allow the daemon to write any updates that were pending at the time of the crash. .Sp On startup, the daemon will check for journal files in this directory. If found, all updates therein will be read into memory before the daemon starts accepting new connections. .Sp The journal will be rotated with the same frequency as the flush timer given by \fB\-f\fR. .Sp When journaling is enabled, the daemon will use a fast shutdown procedure. Rather than flushing all files to disk, it will make sure the journal is properly written and exit immediately. Although the \s-1RRD\s0 data files are not fully up-to-date, no information is lost; all pending updates will be replayed from the journal next time the daemon starts up. .Sp To disable fast shutdown, use the \fB\-F\fR option. .IP "\fB\-F\fR" 4 .IX Item "-F" \&\s-1ALWAYS\s0 flush all updates to the \s-1RRD\s0 data files when the daemon is shut down, regardless of journal setting. .IP "\fB\-g\fR" 4 .IX Item "-g" Run in the foreground. The daemon will not \fIfork()\fR. .IP "\fB\-b\fR \fIdir\fR" 4 .IX Item "-b dir" The daemon will change into a specific directory at startup. All files passed to the daemon, that are specified by a \fBrelative\fR path, will be interpreted to be relative to this directory. If not given the default, \f(CW\*(C`/tmp\*(C'\fR, will be used. .Sp .Vb 10 \& +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ \& ! Command line ! File updated ! \& +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ \& ! foo.rrd ! /tmp/foo.rrd ! \& ! foo/bar.rrd ! /tmp/foo/bar.rrd ! \& ! /var/lib/rrd/foo.rrd ! /var/lib/rrd/foo.rrd ! \& +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ \& Paths given on the command line and paths actually \& updated by the daemon, assuming the base directory \& "/tmp". .Ve .Sp \&\fB\s-1WARNING:\s0\fR The paths up to and including the base directory \fB\s-1MUST\s0 \s-1NOT\s0 \s-1BE\s0\fR symbolic links. In other words, if the base directory is specified as: .Sp .Vb 1 \& \-b /base/dir/somewhere .Ve .Sp \&... then \fB\s-1NONE\s0\fR of the following should be symbolic links: .Sp .Vb 3 \& /base \& /base/dir \& /base/dir/somewhere .Ve .IP "\fB\-B\fR" 4 .IX Item "-B" Only permit writes into the base directory specified in \fB\-b\fR (and any sub-directories). This does \fB\s-1NOT\s0\fR detect symbolic links. Paths containing \f(CW\*(C`../\*(C'\fR will also be blocked. .SH "AFFECTED RRDTOOL COMMANDS" .IX Header "AFFECTED RRDTOOL COMMANDS" The following commands may be made aware of the \fBrrdcached\fR using the command line argument \fB\-\-daemon\fR or the environment variable \fB\s-1RRDCACHED_ADDRESS\s0\fR: .IP "\(bu" 4 dump .IP "\(bu" 4 fetch .IP "\(bu" 4 flush .IP "\(bu" 4 graph .IP "\(bu" 4 graphv .IP "\(bu" 4 info .IP "\(bu" 4 last .IP "\(bu" 4 lastupdate .IP "\(bu" 4 update .IP "\(bu" 4 xport .PP The \fBupdate\fR command can send values to the daemon instead of writing them to the disk itself. All other commands can send a \fB\s-1FLUSH\s0\fR command (see below) to the daemon before accessing the files, so they work with up-to-date data even if the cache timeout is large. .SH "ERROR REPORTING" .IX Header "ERROR REPORTING" The daemon reports errors in one of two ways: During startup, error messages are printed to \f(CW\*(C`STDERR\*(C'\fR. One of the steps when starting up is to fork to the background and closing \f(CW\*(C`STDERR\*(C'\fR \- after this writing directly to the user is no longer possible. Once this has happened, the daemon will send log messages to the system logging daemon using \fIsyslog\fR\|(3). The facility used is \&\f(CW\*(C`LOG_DAEMON\*(C'\fR. .SH "HOW IT WORKS" .IX Header "HOW IT WORKS" When receiving an update, \fBrrdcached\fR does not write to disk but looks for an entry for that file in its internal tree. If not found, an entry is created including the current time (called \*(L"First\*(R" in the diagram below). This time is \&\fBnot\fR the time specified on the command line but the time the operating system considers to be \*(L"now\*(R". The value and time of the value (called \*(L"Time\*(R" in the diagram below) are appended to the tree node. .PP When appending a value to a tree node, it is checked whether it's time to write the values to disk. Values are written to disk if \&\f(CW\*(C`now()\ \-\ First\ >=\ timeout\*(C'\fR, where \f(CW\*(C`timeout\*(C'\fR is the timeout specified using the \fB\-w\fR option, see \*(L"\s-1OPTIONS\s0\*(R". If the values are \*(L"old enough\*(R" they will be enqueued in the \*(L"update queue\*(R", i.\ e. they will be appended to the linked list shown below. Because the tree nodes and the elements of the linked list are the same data structures in memory, any update to a file that has already been enqueued will be written with the next write to the \s-1RRD\s0 file, too. .PP A separate \*(L"update thread\*(R" constantly dequeues the first element in the update queue and writes all its values to the appropriate file. So as long as the update queue is not empty files are written at the highest possible rate. .PP Since the timeout of files is checked only when new values are added to the file, \*(L"dead\*(R" files, i.\ e. files that are not updated anymore, would never be written to disk. Therefore, every now and then, controlled by the \fB\-f\fR option, the entire tree is walked and all \*(L"old\*(R" values are enqueued. Since this only affects \*(L"dead\*(R" files and walking the tree is relatively expensive, you should set the \*(L"flush interval\*(R" to a reasonably high value. The default is 3600\ seconds (one hour). .PP The downside of caching values is that they won't show up in graphs generated from the \s-1RRD\s0\ files. To get around this, the daemon provides the \*(L"flush command\*(R" to flush specific files. This means that the file is inserted at the \&\fBhead\fR of the update queue or moved there if it is already enqueued. The flush command will return only after the file's pending updates have been written to disk. .PP .Vb 10 \& +\-\-\-\-\-\-+ +\-\-\-\-\-\-+ +\-\-\-\-\-\-+ \& ! head ! ! root ! ! tail ! \& +\-\-\-+\-\-+ +\-\-\-+\-\-+ +\-\-\-+\-\-+ \& ! /\e ! \& ! / \e ! \& ! /\e /\e ! \& ! /\e/\e \e \`\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- ... \-\-\-\-\-\-\-\-, ! \& V / \`\-\-\-\-\-\-\-, ! V \& +\-\-\-+\-\-\-\-+\-\-\-+ +\-\-\-\-\-\-+\-\-\-\-\-+ +\-\-\-+\-\-\-\-+\-\-\-+ \& ! File: foo ! ! File: bar ! ! File: qux ! \& ! First: 101 ! ! First: 119 ! ! First: 180 ! \& ! Next:&bar \-+\-\-\->! Next:&... \-+\-\-\-> ... \-\-\->! Next:NULL ! \& | Prev:NULL !<\-\-\-+\-Prev:&foo !<\-\-\- ... \-\-\-\-+\-Prev: &... ! \& +============+ +============+ +============+ \& ! Time: 100 ! ! Time: 120 ! ! Time: 180 ! \& ! Value: 10 ! ! Value: 0.1 ! ! Value: 2,2 ! \& +\-\-\-\-\-\-\-\-\-\-\-\-+ +\-\-\-\-\-\-\-\-\-\-\-\-+ +\-\-\-\-\-\-\-\-\-\-\-\-+ \& ! Time: 110 ! ! Time: 130 ! ! Time: 190 ! \& ! Value: 26 ! ! Value: 0.1 ! ! Value: 7,3 ! \& +\-\-\-\-\-\-\-\-\-\-\-\-+ +\-\-\-\-\-\-\-\-\-\-\-\-+ +\-\-\-\-\-\-\-\-\-\-\-\-+ \& : : : : : : \& +\-\-\-\-\-\-\-\-\-\-\-\-+ +\-\-\-\-\-\-\-\-\-\-\-\-+ +\-\-\-\-\-\-\-\-\-\-\-\-+ \& ! Time: 230 ! ! Time: 250 ! ! Time: 310 ! \& ! Value: 42 ! ! Value: 0.2 ! ! Value: 1,2 ! \& +\-\-\-\-\-\-\-\-\-\-\-\-+ +\-\-\-\-\-\-\-\-\-\-\-\-+ +\-\-\-\-\-\-\-\-\-\-\-\-+ .Ve .PP The above diagram demonstrates: .IP "\(bu" 4 Files/values are stored in a (balanced) tree. .IP "\(bu" 4 Tree nodes and entries in the update queue are the same data structure. .IP "\(bu" 4 The local time (\*(L"First\*(R") and the time specified in updates (\*(L"Time\*(R") may differ. .IP "\(bu" 4 Timed out values are inserted at the \*(L"tail\*(R". .IP "\(bu" 4 Explicitly flushed values are inserted at the \*(L"head\*(R". .IP "\(bu" 4 \&\s-1ASCII\s0 art rocks. .SH "SECURITY CONSIDERATIONS" .IX Header "SECURITY CONSIDERATIONS" .SS "Authentication" .IX Subsection "Authentication" If your rrdtool installation was built without libwrap there is no form of authentication for clients connecting to the rrdcache daemon! .PP If your rrdtool installation was built with libwrap then you can use hosts_access to restrict client access to the rrdcache daemon (rrdcached). For more information on how to use hosts_access to restrict access to the rrdcache daemon you should read the \fIhosts_access\fR\|(5) man pages. .PP It is still highly recommended to install a packet filter or similar mechanism to prevent unauthorized connections. Unless you have a dedicated \s-1VLAN\s0 or \s-1VPN\s0 for this, using network sockets is probably a bad idea! .SS "Authorization" .IX Subsection "Authorization" There is minimal per-socket authorization. .PP Authorization is currently done on a per-socket basis. That means each socket has a list of commands it will accept and it will accept. It will accept only those commands explicitly listed but it will (currently) accept these commands from anyone reaching the socket. .PP If the networking sockets are to be used, it is necessary to restrict the accepted commands to those needed by external clients. If, for example, external clients want to draw graphs of the cached data, they should only be allowed to use the \f(CW\*(C`FLUSH\*(C'\fR command. .SS "Encryption" .IX Subsection "Encryption" There is no encryption. .PP Again, this may be added in the future, but for the time being it is your job to keep your private data private. Install a \s-1VPN\s0 or an encrypted tunnel if you statistics are confidential! .SS "Sanity checking" .IX Subsection "Sanity checking" There is no sanity checking. .PP The daemon will blindly write to any file it gets told, so you really should create a separate user just for this daemon. Also it does not do any sanity checks, so if it gets told to write values for a time far in the future, your files will be messed up good! .SS "Conclusion" .IX Subsection "Conclusion" .IP "\(bu" 4 Security is the job of the administrator. .IP "\(bu" 4 We recommend to allow write access via \s-1UNIX\s0 domain sockets only. .IP "\(bu" 4 You have been warned. .SH "PROTOCOL" .IX Header "PROTOCOL" The daemon communicates with clients using a line based \s-1ASCII\s0 protocol which is easy to read and easy to type. This makes it easy for scripts to implement the protocol and possible for users to use telnet to connect to the daemon and test stuff \*(L"by hand\*(R". .PP The protocol is line based, this means that each record consists of one or more lines. A line is terminated by the line feed character \f(CW0x0A\fR, commonly written as \f(CW\*(C`\en\*(C'\fR. In the examples below, this character will be written as \&\f(CW\*(C`\*(C'\fR (\*(L"line feed\*(R"). .PP After the connection has been established, the client is expected to send a \&\*(L"command\*(R". A command consists of the command keyword, possibly some arguments, and a terminating newline character. For a list of commands, see \&\*(L"Valid Commands\*(R" below. .PP Example: .PP .Vb 1 \& FLUSH /tmp/foo.rrd .Ve .PP The daemon answers with a line consisting of a status code and a short status message, separated by one or more space characters. A negative status code signals an error, a positive status code or zero signal success. If the status code is greater than zero, it indicates the number of lines that follow the status line. .PP Examples: .PP .Vb 1 \& 0 Success \& \& 2 Two lines follow \& This is the first line \& And this is the second line .Ve .SS "Valid Commands" .IX Subsection "Valid Commands" The following commands are understood by the daemon: .IP "\fB\s-1FLUSH\s0\fR \fIfilename\fR" 4 .IX Item "FLUSH filename" Causes the daemon to put \fIfilename\fR to the \fBhead\fR of the update queue (possibly moving it there if the node is already enqueued). The answer will be sent \fBafter\fR the node has been dequeued. .IP "\fB\s-1FLUSHALL\s0\fR" 4 .IX Item "FLUSHALL" Causes the daemon to start flushing \s-1ALL\s0 pending values to disk. This returns immediately, even though the writes may take a long time. .IP "\fB\s-1PENDING\s0\fR \fIfilename\fR" 4 .IX Item "PENDING filename" Shows any \*(L"pending\*(R" updates for a file, in order. The updates shown have not yet been written to the underlying \s-1RRD\s0 file. .IP "\fB\s-1FORGET\s0\fR \fIfilename\fR" 4 .IX Item "FORGET filename" Removes \fIfilename\fR from the cache. Any pending updates \fB\s-1WILL\s0 \s-1BE\s0 \s-1LOST\s0\fR. .IP "\fB\s-1QUEUE\s0\fR" 4 .IX Item "QUEUE" Shows the files that are on the output queue. Returns zero or more lines in the following format, where is the number of values to be written for the : .Sp .Vb 1 \& .Ve .IP "\fB\s-1HELP\s0\fR [\fIcommand\fR]" 4 .IX Item "HELP [command]" Returns a short usage message. If no command is given, or \fIcommand\fR is \&\fB\s-1HELP\s0\fR, a list of commands supported by the daemon is returned. Otherwise a short description, possibly containing a pointer to a manual page, is returned. Obviously, this is meant for interactive usage and the format in which the commands and usage summaries are returned is not well defined. .IP "\fB\s-1STATS\s0\fR" 4 .IX Item "STATS" Returns a list of metrics which can be used to measure the daemons performance and check its status. For a description of the values returned, see \&\*(L"Performance Values\*(R" below. .Sp The format in which the values are returned is similar to many other line based protocols: Each value is printed on a separate line, each consisting of the name of the value, a colon, one or more spaces and the actual value. .Sp Example: .Sp .Vb 10 \& 9 Statistics follow \& QueueLength: 0 \& UpdatesReceived: 30 \& FlushesReceived: 2 \& UpdatesWritten: 13 \& DataSetsWritten: 390 \& TreeNodesNumber: 13 \& TreeDepth: 4 \& JournalBytes: 190 \& JournalRotate: 0 .Ve .IP "\fB\s-1UPDATE\s0\fR \fIfilename\fR \fIvalues\fR [\fIvalues\fR ...]" 4 .IX Item "UPDATE filename values [values ...]" Adds more data to a filename. This is \fBthe\fR operation the daemon was designed for, so describing the mechanism again is unnecessary. Read \*(L"\s-1HOW\s0 \s-1IT\s0 \s-1WORKS\s0\*(R" above for a detailed explanation. .Sp Note that rrdcached only accepts absolute timestamps in the update values. Updates strings like \*(L"N:1:2:3\*(R" are automatically converted to absolute time by the \s-1RRD\s0 client library before sending to rrdcached. .IP "\fB\s-1WROTE\s0\fR \fIfilename\fR" 4 .IX Item "WROTE filename" This command is written to the journal after a file is successfully written out to disk. It is used during journal replay to determine which updates have already been applied. It is \fIonly\fR valid in the journal; it is not accepted from the other command channels. .IP "\fB\s-1BATCH\s0\fR" 4 .IX Item "BATCH" This command initiates the bulk load of multiple commands. This is designed for installations with extremely high update rates, since it permits more than one command to be issued per \fIread()\fR and \fIwrite()\fR. .Sp All commands are executed just as they would be if given individually, except for output to the user. Messages indicating success are suppressed, and error messages are delayed until the client is finished. .Sp Command processing is finished when the client sends a dot (\*(L".\*(R") on its own line. After the client has finished, the server responds with an error count and the list of error messages (if any). Each error messages indicates the number of the command to which it corresponds, and the error message itself. The first user command after \fB\s-1BATCH\s0\fR is command number one. .Sp .Vb 9 \& client: BATCH \& server: 0 Go ahead. End with dot \*(Aq.\*(Aq on its own line. \& client: UPDATE x.rrd 1223661439:1:2:3 <\-\-\- command #1 \& client: UPDATE y.rrd 1223661440:3:4:5 <\-\-\- command #2 \& client: and so on... \& client: . \& server: 2 Errors \& server: 1 message for command 1 \& server: 12 message for command 12 .Ve .IP "\fB\s-1QUIT\s0\fR" 4 .IX Item "QUIT" Disconnect from rrdcached. .SS "Performance Values" .IX Subsection "Performance Values" The following counters are returned by the \fB\s-1STATS\s0\fR command: .IP "\fBQueueLength\fR \fI(unsigned 64bit integer)\fR" 4 .IX Item "QueueLength (unsigned 64bit integer)" Number of nodes currently enqueued in the update queue. .IP "\fBUpdatesReceived\fR \fI(unsigned 64bit integer)\fR" 4 .IX Item "UpdatesReceived (unsigned 64bit integer)" Number of \s-1UPDATE\s0 commands received. .IP "\fBFlushesReceived\fR \fI(unsigned 64bit integer)\fR" 4 .IX Item "FlushesReceived (unsigned 64bit integer)" Number of \s-1FLUSH\s0 commands received. .IP "\fBUpdatesWritten\fR \fI(unsigned 64bit integer)\fR" 4 .IX Item "UpdatesWritten (unsigned 64bit integer)" Total number of updates, i.\ e. calls to \f(CW\*(C`rrd_update_r\*(C'\fR, since the daemon was started. .IP "\fBDataSetsWritten\fR \fI(unsigned 64bit integer)\fR" 4 .IX Item "DataSetsWritten (unsigned 64bit integer)" Total number of \*(L"data sets\*(R" written to disk since the daemon was started. A data set is one or more values passed to the \fB\s-1UPDATE\s0\fR command. For example: \f(CW\*(C`1223661439:123:456\*(C'\fR is one data set with two values. The term \*(L"data set\*(R" is used to prevent confusion whether individual values or groups of values are counted. .IP "\fBTreeNodesNumber\fR \fI(unsigned 64bit integer)\fR" 4 .IX Item "TreeNodesNumber (unsigned 64bit integer)" Number of nodes in the cache. .IP "\fBTreeDepth\fR \fI(unsigned 64bit integer)\fR" 4 .IX Item "TreeDepth (unsigned 64bit integer)" Depth of the tree used for fast key lookup. .IP "\fBJournalBytes\fR \fI(unsigned 64bit integer)\fR" 4 .IX Item "JournalBytes (unsigned 64bit integer)" Total number of bytes written to the journal since startup. .IP "\fBJournalRotate\fR \fI(unsigned 64bit integer)\fR" 4 .IX Item "JournalRotate (unsigned 64bit integer)" Number of times the journal has been rotated since startup. .SH "SIGNALS" .IX Header "SIGNALS" .IP "\s-1SIGINT\s0 and \s-1SIGTERM\s0" 4 .IX Item "SIGINT and SIGTERM" The daemon exits normally on receipt of either of these signals. Pending updates are handled in accordance with the \fB\-j\fR and \fB\-F\fR options. .IP "\s-1SIGUSR1\s0" 4 .IX Item "SIGUSR1" The daemon exits \s-1AFTER\s0 flushing all updates out to disk. This may take a while. .IP "\s-1SIGUSR2\s0" 4 .IX Item "SIGUSR2" The daemon exits immediately, without flushing updates out to disk. Pending updates will be replayed from the journal when the daemon starts up again. \fB\s-1WARNING:\s0 if journaling (\-j) is \s-1NOT\s0 enabled, any pending updates \s-1WILL\s0 \s-1BE\s0 \s-1LOST\s0\fR. .SH "BUGS" .IX Header "BUGS" No known bugs at the moment. .SH "SEE ALSO" .IX Header "SEE ALSO" rrdtool, rrdgraph .SH "AUTHOR" .IX Header "AUTHOR" Florian Forster .PP Both \fBrrdcached\fR and this manual page have been written by Florian. .SH "CONTRIBUTORS" .IX Header "CONTRIBUTORS" kevin brintnall rrdtool-1.4.7/doc/rrdtool.10000644000175300017510000004143711707501547012471 00000000000000.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.07) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "RRDTOOL 1" .TH RRDTOOL 1 "2009-10-14" "1.4.7" "rrdtool" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" rrdtool \- Round Robin Database Tool .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBrrdtool\fR \fB\-\fR [workdir]| \fIfunction\fR .SH "DESCRIPTION" .IX Header "DESCRIPTION" .SS "\s-1OVERVIEW\s0" .IX Subsection "OVERVIEW" It is pretty easy to gather status information from all sorts of things, ranging from the temperature in your office to the number of octets which have passed through the \s-1FDDI\s0 interface of your router. But it is not so trivial to store this data in an efficient and systematic manner. This is where \fBRRDtool\fR comes in handy. It lets you \&\fIlog and analyze\fR the data you gather from all kinds of data-sources (\fB\s-1DS\s0\fR). The data analysis part of RRDtool is based on the ability to quickly generate graphical representations of the data values collected over a definable time period. .PP In this man page you will find general information on the design and functionality of the Round Robin Database Tool (RRDtool). For a more detailed description of how to use the individual functions of \&\fBRRDtool\fR check the corresponding man page. .PP For an introduction to the usage of RRDtool make sure you consult the rrdtutorial. .SS "\s-1FUNCTIONS\s0" .IX Subsection "FUNCTIONS" While the man pages talk of command line switches you have to set in order to make \fBRRDtool\fR work it is important to note that \&\fBRRDtool\fR can be remotely controlled through a set of pipes. This saves a considerable amount of startup time when you plan to make \&\fBRRDtool\fR do a lot of things quickly. Check the section on Remote_Control further down. There is also a number of language bindings for RRDtool which allow you to use it directly from Perl, python, Tcl, \&\s-1PHP\s0, etc. .IP "\fBcreate\fR" 8 .IX Item "create" Set up a new Round Robin Database (\s-1RRD\s0). Check rrdcreate. .IP "\fBupdate\fR" 8 .IX Item "update" Store new data values into an \s-1RRD\s0. Check rrdupdate. .IP "\fBupdatev\fR" 8 .IX Item "updatev" Operationally equivalent to \fBupdate\fR except for output. Check rrdupdate. .IP "\fBgraph\fR" 8 .IX Item "graph" Create a graph from data stored in one or several RRDs. Apart from generating graphs, data can also be extracted to stdout. Check rrdgraph. .IP "\fBdump\fR" 8 .IX Item "dump" Dump the contents of an \s-1RRD\s0 in plain \s-1ASCII\s0. In connection with restore you can use this to move an \s-1RRD\s0 from one computer architecture to another. Check rrddump. .IP "\fBrestore\fR" 8 .IX Item "restore" Restore an \s-1RRD\s0 in \s-1XML\s0 format to a binary \s-1RRD\s0. Check rrdrestore .IP "\fBfetch\fR" 8 .IX Item "fetch" Get data for a certain time period from a \s-1RRD\s0. The graph function uses fetch to retrieve its data from an \s-1RRD\s0. Check rrdfetch. .IP "\fBtune\fR" 8 .IX Item "tune" Alter setup of an \s-1RRD\s0. Check rrdtune. .IP "\fBlast\fR" 8 .IX Item "last" Find the last update time of an \s-1RRD\s0. Check rrdlast. .IP "\fBinfo\fR" 8 .IX Item "info" Get information about an \s-1RRD\s0. Check rrdinfo. .IP "\fBrrdresize\fR" 8 .IX Item "rrdresize" Change the size of individual RRAs. This is dangerous! Check rrdresize. .IP "\fBxport\fR" 8 .IX Item "xport" Export data retrieved from one or several RRDs. Check rrdxport. .IP "\fBflushcached\fR" 8 .IX Item "flushcached" Flush the values for a specific \s-1RRD\s0 file from memory. Check rrdflushcached. .IP "\fBrrdcgi\fR" 8 .IX Item "rrdcgi" This is a standalone tool for producing \s-1RRD\s0 graphs on the fly. Check rrdcgi. .SS "\s-1HOW\s0 \s-1DOES\s0 \s-1RRDTOOL\s0 \s-1WORK\s0?" .IX Subsection "HOW DOES RRDTOOL WORK?" .IP "Data Acquisition" 8 .IX Item "Data Acquisition" When monitoring the state of a system, it is convenient to have the data available at a constant time interval. Unfortunately, you may not always be able to fetch data at exactly the time you want to. Therefore \fBRRDtool\fR lets you update the log file at any time you want. It will automatically interpolate the value of the data-source (\fB\s-1DS\s0\fR) at the latest official time-slot (interval) and write this interpolated value to the log. The original value you have supplied is stored as well and is also taken into account when interpolating the next log entry. .IP "Consolidation" 8 .IX Item "Consolidation" You may log data at a 1 minute interval, but you might also be interested to know the development of the data over the last year. You could do this by simply storing the data in 1 minute intervals for the whole year. While this would take considerable disk space it would also take a lot of time to analyze the data when you wanted to create a graph covering the whole year. \fBRRDtool\fR offers a solution to this problem through its data consolidation feature. When setting up an Round Robin Database (\fB\s-1RRD\s0\fR), you can define at which interval this consolidation should occur, and what consolidation function (\fB\s-1CF\s0\fR) (average, minimum, maximum, total, last) should be used to build the consolidated values (see rrdcreate). You can define any number of different consolidation setups within one \fB\s-1RRD\s0\fR. They will all be maintained on the fly when new data is loaded into the \fB\s-1RRD\s0\fR. .IP "Round Robin Archives" 8 .IX Item "Round Robin Archives" Data values of the same consolidation setup are stored into Round Robin Archives (\fB\s-1RRA\s0\fR). This is a very efficient manner to store data for a certain amount of time, while using a known and constant amount of storage space. .Sp It works like this: If you want to store 1'000 values in 5 minute interval, \fBRRDtool\fR will allocate space for 1'000 data values and a header area. In the header it will store a pointer telling which slots (value) in the storage area was last written to. New values are written to the Round Robin Archive in, you guessed it, a round robin manner. This automatically limits the history to the last 1'000 values (in our example). Because you can define several \fB\s-1RRA\s0\fRs within a single \fB\s-1RRD\s0\fR, you can setup another one, for storing 750 data values at a 2 hour interval, for example, and thus keep a log for the last two months at a lower resolution. .Sp The use of \fB\s-1RRA\s0\fRs guarantees that the \fB\s-1RRD\s0\fR does not grow over time and that old data is automatically eliminated. By using the consolidation feature, you can still keep data for a very long time, while gradually reducing the resolution of the data along the time axis. .Sp Using different consolidation functions (\fB\s-1CF\s0\fR) allows you to store exactly the type of information that actually interests you: the maximum one minute traffic on the \s-1LAN\s0, the minimum temperature of your wine cellar, the total minutes of down time, etc. .IP "Unknown Data" 8 .IX Item "Unknown Data" As mentioned earlier, the \fB\s-1RRD\s0\fR stores data at a constant interval. Sometimes it may happen that no new data is available when a value has to be written to the \fB\s-1RRD\s0\fR. Data acquisition may not be possible for one reason or other. With \fBRRDtool\fR you can handle these situations by storing an \fI*UNKNOWN*\fR value into the database. The value '\fI*UNKNOWN*\fR' is supported through all the functions of the tool. When consolidating a data set, the amount of \fI*UNKNOWN*\fR data values is accounted for and when a new consolidated value is ready to be written to its Round Robin Archive (\fB\s-1RRA\s0\fR), a validity check is performed to make sure that the percentage of unknown values in the data point is above a configurable level. If not, an \fI*UNKNOWN*\fR value will be written to the \fB\s-1RRA\s0\fR. .IP "Graphing" 8 .IX Item "Graphing" \&\fBRRDtool\fR allows you to generate reports in numerical and graphical form based on the data stored in one or several \&\fB\s-1RRD\s0\fRs. The graphing feature is fully configurable. Size, color and contents of the graph can be defined freely. Check rrdgraph for more information on this. .IP "Aberrant Behavior Detection" 8 .IX Item "Aberrant Behavior Detection" by Jake Brutlag .Sp \&\fBRRDtool\fR provides the building blocks for near real-time aberrant behavior detection. These components include: .RS 8 .IP "\(bu" 4 An algorithm for predicting the value of a time series one time step into the future. .IP "\(bu" 4 A measure of deviation between predicted and observed values. .IP "\(bu" 4 A mechanism to decide if and when an observed value or sequence of observed values is \fItoo deviant\fR from the predicted value(s). .RE .RS 8 .Sp Here is a brief explanation of these components: .Sp The Holt-Winters time series forecasting algorithm is an on-line (or incremental) algorithm that adaptively predicts future observations in a time series. Its forecast is the sum of three components: a baseline (or intercept), a linear trend over time (or slope), and a seasonal coefficient (a periodic effect, such as a daily cycle). There is one seasonal coefficient for each time point in the period (cycle). After a value is observed, each of these components is updated via exponential smoothing. This means that the algorithm \*(L"learns\*(R" from past values and uses them to predict the future. The rate of adaptation is governed by 3 parameters, alpha (intercept), beta (slope), and gamma (seasonal). The prediction can also be viewed as a smoothed value for the time series. .Sp The measure of deviation is a seasonal weighted absolute deviation. The term \fIseasonal\fR means deviation is measured separately for each time point in the seasonal cycle. As with Holt-Winters forecasting, deviation is predicted using the measure computed from past values (but only at that point in the seasonal cycle). After the value is observed, the algorithm learns from the observed value via exponential smoothing. Confidence bands for the observed time series are generated by scaling the sequence of predicted deviation values (we usually think of the sequence as a continuous line rather than a set of discrete points). .Sp Aberrant behavior (a potential failure) is reported whenever the number of times the observed value violates the confidence bands meets or exceeds a specified threshold within a specified temporal window (e.g. 5 violations during the past 45 minutes with a value observed every 5 minutes). .Sp This functionality is embedded in a set of related \fBRRAs\fR. In particular, a \s-1FAILURES\s0 \fB\s-1RRA\s0\fR logs potential failures. With these data you could, for example, use a front-end application to \fBRRDtool\fR to initiate real-time alerts. .Sp For a detailed description on how to set this up, see rrdcreate. .RE .SS "\s-1REMOTE\s0 \s-1CONTROL\s0" .IX Subsection "REMOTE CONTROL" When you start \fBRRDtool\fR with the command line option '\fB\-\fR' it waits for input via standard input (\s-1STDIN\s0). With this feature you can improve performance by attaching \fBRRDtool\fR to another process (\s-1MRTG\s0 is one example) through a set of pipes. Over these pipes \fBRRDtool\fR accepts the same arguments as on the command line and some special commands like \fBquit, cd, mkdir\fR and \fBls\fR. For detailed help on the server commands type: .PP .Vb 1 \& rrdtool help cd|mkdir|pwd|ls|quit .Ve .PP When a command is completed, RRDtool will print the string '\f(CW\*(C`OK\*(C'\fR', followed by timing information of the form \fBu:\fR\fIusertime\fR \&\fBs:\fR\fIsystemtime\fR. Both values are the running totals of seconds since RRDtool was started. If an error occurs, a line of the form '\f(CW\*(C`ERROR:\*(C'\fR \&\fIDescription of error\fR' will be printed instead. \fBRRDtool\fR will not abort, unless something really serious happens. If a \fBworkdir\fR is specified and the \s-1UID\s0 is 0, RRDtool will do a chroot to that workdir. If the \s-1UID\s0 is not 0, RRDtool only changes the current directory to \&\fBworkdir\fR. .SS "\s-1RRD\s0 Server" .IX Subsection "RRD Server" If you want to create a RRD-Server, you must choose a \s-1TCP/IP\s0 Service number and add them to \fI/etc/services\fR like this: .PP .Vb 1 \& rrdsrv 13900/tcp # RRD server .Ve .PP Attention: the \s-1TCP\s0 port 13900 isn't officially registered for rrdsrv. You can use any unused port in your services file, but the server and the client system must use the same port, of course. .PP With this configuration you can add RRDtool as meta-server to \&\fI/etc/inetd.conf\fR. For example: .PP .Vb 1 \& rrdsrv stream tcp nowait root /opt/rrd/bin/rrdtool rrdtool \- /var/rrd .Ve .PP Don't forget to create the database directory /var/rrd and reinitialize your inetd. .PP If all was setup correctly, you can access the server with Perl sockets, tools like netcat, or in a quick interactive test by using \&'telnet localhost rrdsrv'. .PP \&\fB\s-1NOTE:\s0\fR that there is no authentication with this feature! Do not setup such a port unless you are sure what you are doing. .SH "RRDCACHED, THE CACHING DAEMON" .IX Header "RRDCACHED, THE CACHING DAEMON" For very big setups, updating thousands of \s-1RRD\s0 files often becomes a serious \s-1IO\s0 problem. If you run into such problems, you might want to take a look at rrdcached, a caching daemon for RRDtool which may help you lessen the stress on your disks. .SH "SEE ALSO" .IX Header "SEE ALSO" rrdcreate, rrdupdate, rrdgraph, rrddump, rrdfetch, rrdtune, rrdlast, rrdxport, rrdflushcached, rrdcached .SH "BUGS" .IX Header "BUGS" Bugs? Features! .SH "AUTHOR" .IX Header "AUTHOR" Tobias Oetiker rrdtool-1.4.7/doc/rrdgraph_rpn.10000644000175300017510000004720511707501547013473 00000000000000.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.07) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "RRDGRAPH_RPN 1" .TH RRDGRAPH_RPN 1 "2012-01-18" "1.4.7" "rrdtool" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" rrdgraph_rpn \- About RPN Math in rrdtool graph .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fI\s-1RPN\s0 expression\fR:=\fIvname\fR|\fIoperator\fR|\fIvalue\fR[,\fI\s-1RPN\s0 expression\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" If you have ever used a traditional \s-1HP\s0 calculator you already know \&\fB\s-1RPN\s0\fR (Reverse Polish Notation). The idea behind \fB\s-1RPN\s0\fR is that you have a stack and push your data onto this stack. Whenever you execute an operation, it takes as many elements from the stack as needed. Pushing is done implicitly, so whenever you specify a number or a variable, it gets pushed onto the stack automatically. .PP At the end of the calculation there should be one and only one value left on the stack. This is the outcome of the function and this is what is put into the \fIvname\fR. For \fB\s-1CDEF\s0\fR instructions, the stack is processed for each data point on the graph. \fB\s-1VDEF\s0\fR instructions work on an entire data set in one run. Note, that currently \fB\s-1VDEF\s0\fR instructions only support a limited list of functions. .PP Example: \f(CW\*(C`VDEF:maximum=mydata,MAXIMUM\*(C'\fR .PP This will set variable \*(L"maximum\*(R" which you now can use in the rest of your \s-1RRD\s0 script. .PP Example: \f(CW\*(C`CDEF:mydatabits=mydata,8,*\*(C'\fR .PP This means: push variable \fImydata\fR, push the number 8, execute the operator \fI*\fR. The operator needs two elements and uses those to return one value. This value is then stored in \fImydatabits\fR. As you may have guessed, this instruction means nothing more than \&\fImydatabits = mydata * 8\fR. The real power of \fB\s-1RPN\s0\fR lies in the fact that it is always clear in which order to process the input. For expressions like \f(CW\*(C`a = b + 3 * 5\*(C'\fR you need to multiply 3 with 5 first before you add \fIb\fR to get \fIa\fR. However, with parentheses you could change this order: \f(CW\*(C`a = (b + 3) * 5\*(C'\fR. In \fB\s-1RPN\s0\fR, you would do \f(CW\*(C`a = b, 3, +, 5, *\*(C'\fR without the need for parentheses. .SH "OPERATORS" .IX Header "OPERATORS" .IP "Boolean operators" 4 .IX Item "Boolean operators" \&\fB\s-1LT\s0, \s-1LE\s0, \s-1GT\s0, \s-1GE\s0, \s-1EQ\s0, \s-1NE\s0\fR .Sp Pop two elements from the stack, compare them for the selected condition and return 1 for true or 0 for false. Comparing an \fIunknown\fR or an \&\fIinfinite\fR value will result in \fIunknown\fR returned ... which will also be treated as false by the \fB\s-1IF\s0\fR call. .Sp \&\fB\s-1UN\s0, \s-1ISINF\s0\fR .Sp Pop one element from the stack, compare this to \fIunknown\fR respectively to \fIpositive or negative infinity\fR. Returns 1 for true or 0 for false. .Sp \&\fB\s-1IF\s0\fR .Sp Pops three elements from the stack. If the element popped last is 0 (false), the value popped first is pushed back onto the stack, otherwise the value popped second is pushed back. This does, indeed, mean that any value other than 0 is considered to be true. .Sp Example: \f(CW\*(C`A,B,C,IF\*(C'\fR should be read as \f(CW\*(C`if (A) then (B) else (C)\*(C'\fR .Sp .IP "Comparing values" 4 .IX Item "Comparing values" \&\fB\s-1MIN\s0, \s-1MAX\s0\fR .Sp Pops two elements from the stack and returns the smaller or larger, respectively. Note that \fIinfinite\fR is larger than anything else. If one of the input numbers is \fIunknown\fR then the result of the operation will be \&\fIunknown\fR too. .Sp \&\fB\s-1LIMIT\s0\fR .Sp Pops two elements from the stack and uses them to define a range. Then it pops another element and if it falls inside the range, it is pushed back. If not, an \fIunknown\fR is pushed. .Sp The range defined includes the two boundaries (so: a number equal to one of the boundaries will be pushed back). If any of the three numbers involved is either \fIunknown\fR or \fIinfinite\fR this function will always return an \fIunknown\fR .Sp Example: \f(CW\*(C`CDEF:a=alpha,0,100,LIMIT\*(C'\fR will return \fIunknown\fR if alpha is lower than 0 or if it is higher than 100. .Sp .IP "Arithmetics" 4 .IX Item "Arithmetics" \&\fB+, \-, *, /, %\fR .Sp Add, subtract, multiply, divide, modulo .Sp \&\fB\s-1ADDNAN\s0\fR .Sp NAN-safe addition. If one parameter is \s-1NAN/UNKNOWN\s0 it'll be treated as zero. If both parameters are \s-1NAN/UNKNOWN\s0, \s-1NAN/UNKNOWN\s0 will be returned. .Sp \&\fB\s-1SIN\s0, \s-1COS\s0, \s-1LOG\s0, \s-1EXP\s0, \s-1SQRT\s0\fR .Sp Sine and cosine (input in radians), log and exp (natural logarithm), square root. .Sp \&\fB\s-1ATAN\s0\fR .Sp Arctangent (output in radians). .Sp \&\fB\s-1ATAN2\s0\fR .Sp Arctangent of y,x components (output in radians). This pops one element from the stack, the x (cosine) component, and then a second, which is the y (sine) component. It then pushes the arctangent of their ratio, resolving the ambiguity between quadrants. .Sp Example: \f(CW\*(C`CDEF:angle=Y,X,ATAN2,RAD2DEG\*(C'\fR will convert \f(CW\*(C`X,Y\*(C'\fR components into an angle in degrees. .Sp \&\fB\s-1FLOOR\s0, \s-1CEIL\s0\fR .Sp Round down or up to the nearest integer. .Sp \&\fB\s-1DEG2RAD\s0, \s-1RAD2DEG\s0\fR .Sp Convert angle in degrees to radians, or radians to degrees. .Sp \&\fB\s-1ABS\s0\fR .Sp Take the absolute value. .IP "Set Operations" 4 .IX Item "Set Operations" \&\fB\s-1SORT\s0, \s-1REV\s0\fR .Sp Pop one element from the stack. This is the \fIcount\fR of items to be sorted (or reversed). The top \fIcount\fR of the remaining elements are then sorted (or reversed) in place on the stack. .Sp Example: \f(CW\*(C`CDEF:x=v1,v2,v3,v4,v5,v6,6,SORT,POP,5,REV,POP,+,+,+,4,/\*(C'\fR will compute the average of the values v1 to v6 after removing the smallest and largest. .Sp \&\fB\s-1AVG\s0\fR .Sp Pop one element (\fIcount\fR) from the stack. Now pop \fIcount\fR elements and build the average, ignoring all \s-1UNKNOWN\s0 values in the process. .Sp Example: \f(CW\*(C`CDEF:x=a,b,c,d,4,AVG\*(C'\fR .Sp \&\fB\s-1TREND\s0, \s-1TRENDNAN\s0\fR .Sp Create a \*(L"sliding window\*(R" average of another data series. .Sp Usage: CDEF:smoothed=x,1800,TREND .Sp This will create a half-hour (1800 second) sliding window average of x. The average is essentially computed as shown here: .Sp .Vb 8 \& +\-\-\-!\-\-\-!\-\-\-!\-\-\-!\-\-\-!\-\-\-!\-\-\-!\-\-\-!\-\-\-> \& now \& delay t0 \& <\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-> \& delay t1 \& <\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-> \& delay t2 \& <\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-> \& \& \& Value at sample (t0) will be the average between (t0\-delay) and (t0) \& Value at sample (t1) will be the average between (t1\-delay) and (t1) \& Value at sample (t2) will be the average between (t2\-delay) and (t2) .Ve .Sp \&\s-1TRENDNAN\s0 is \- in contrast to \s-1TREND\s0 \- NAN-safe. If you use \s-1TREND\s0 and one source value is \s-1NAN\s0 the complete sliding window is affected. The \s-1TRENDNAN\s0 operation ignores all NAN-values in a sliding window and computes the average of the remaining values. .Sp \&\fB\s-1PREDICT\s0, \s-1PREDICTSIGMA\s0\fR .Sp Create a \*(L"sliding window\*(R" average/sigma of another data series, that also shifts the data series by given amounts of of time as well .Sp Usage \- explicit stating shifts: CDEF:predict=,...,,n,,x,PREDICT CDEF:sigma=,...,,n,,x,PREDICTSIGMA .Sp Usage \- shifts defined as a base shift and a number of time this is applied CDEF:predict=,\-n,,x,PREDICT CDEF:sigma=,\-n,,x,PREDICTSIGMA .Sp Example: CDEF:predict=172800,86400,2,1800,x,PREDICT .Sp This will create a half-hour (1800 second) sliding window average/sigma of x, that average is essentially computed as shown here: .Sp .Vb 10 \& +\-\-\-!\-\-\-!\-\-\-!\-\-\-!\-\-\-!\-\-\-!\-\-\-!\-\-\-!\-\-\-!\-\-\-!\-\-\-!\-\-\-!\-\-\-!\-\-\-!\-\-\-!\-\-\-!\-\-\-!\-\-\-> \& now \& shift 1 t0 \& <\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-> \& window \& <\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-> \& shift 2 \& <\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-> \& window \& <\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-> \& shift 1 t1 \& <\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-> \& window \& <\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-> \& shift 2 \& <\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-> \& window \& <\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-> \& \& Value at sample (t0) will be the average between (t0\-shift1\-window) and (t0\-shift1) \& and between (t0\-shift2\-window) and (t0\-shift2) \& Value at sample (t1) will be the average between (t1\-shift1\-window) and (t1\-shift1) \& and between (t1\-shift2\-window) and (t1\-shift2) .Ve .Sp The function is by design NAN-safe. This also allows for extrapolation into the future (say a few days) \&\- you may need to define the data series whit the optional start= parameter, so that the source data series has enough data to provide prediction also at the beginning of a graph... .Sp Here an example, that will create a 10 day graph that also shows the prediction 3 days into the future with its uncertainty value (as defined by avg+\-4*sigma) This also shows if the prediction is exceeded at a certain point. .Sp rrdtool graph image.png \-\-imgformat=PNG \e \-\-start=\-7days \-\-end=+3days \-\-width=1000 \-\-height=200 \-\-alt\-autoscale\-max \e DEF:value=value.rrd:value:AVERAGE:start=\-14days \e LINE1:value#ff0000:value \e CDEF:predict=86400,\-7,1800,value,PREDICT \e CDEF:sigma=86400,\-7,1800,value,PREDICTSIGMA \e CDEF:upper=predict,sigma,3,*,+ \e CDEF:lower=predict,sigma,3,*,\- \e LINE1:predict#00ff00:prediction \e LINE1:upper#0000ff:upper\e certainty\e limit \e LINE1:lower#0000ff:lower\e certainty\e limit \e CDEF:exceeds=value,UN,0,value,lower,upper,LIMIT,UN,IF \e TICK:exceeds#aa000080:1 .Sp Note: Experience has shown that a factor between 3 and 5 to scale sigma is a good discriminator to detect abnormal behavior. This obviously depends also on the type of data and how \*(L"noisy\*(R" the data series is. .Sp This prediction can only be used for short term extrapolations \- say a few days into the future\- .IP "Special values" 4 .IX Item "Special values" \&\fB\s-1UNKN\s0\fR .Sp Pushes an unknown value on the stack .Sp \&\fB\s-1INF\s0, \s-1NEGINF\s0\fR .Sp Pushes a positive or negative infinite value on the stack. When such a value is graphed, it appears at the top or bottom of the graph, no matter what the actual value on the y\-axis is. .Sp \&\fB\s-1PREV\s0\fR .Sp Pushes an \fIunknown\fR value if this is the first value of a data set or otherwise the result of this \fB\s-1CDEF\s0\fR at the previous time step. This allows you to do calculations across the data. This function cannot be used in \fB\s-1VDEF\s0\fR instructions. .Sp \&\fB\s-1PREV\s0(vname)\fR .Sp Pushes an \fIunknown\fR value if this is the first value of a data set or otherwise the result of the vname variable at the previous time step. This allows you to do calculations across the data. This function cannot be used in \fB\s-1VDEF\s0\fR instructions. .Sp \&\fB\s-1COUNT\s0\fR .Sp Pushes the number 1 if this is the first value of the data set, the number 2 if it is the second, and so on. This special value allows you to make calculations based on the position of the value within the data set. This function cannot be used in \fB\s-1VDEF\s0\fR instructions. .IP "Time" 4 .IX Item "Time" Time inside RRDtool is measured in seconds since the epoch. The epoch is defined to be \f(CW\*(C`Thu\ Jan\ \ 1\ 00:00:00\ UTC\ 1970\*(C'\fR. .Sp \&\fB\s-1NOW\s0\fR .Sp Pushes the current time on the stack. .Sp \&\fB\s-1TIME\s0\fR .Sp Pushes the time the currently processed value was taken at onto the stack. .Sp \&\fB\s-1LTIME\s0\fR .Sp Takes the time as defined by \fB\s-1TIME\s0\fR, applies the time zone offset valid at that time including daylight saving time if your \s-1OS\s0 supports it, and pushes the result on the stack. There is an elaborate example in the examples section below on how to use this. .IP "Processing the stack directly" 4 .IX Item "Processing the stack directly" \&\fB\s-1DUP\s0, \s-1POP\s0, \s-1EXC\s0\fR .Sp Duplicate the top element, remove the top element, exchange the two top elements. .Sp .SH "VARIABLES" .IX Header "VARIABLES" These operators work only on \fB\s-1VDEF\s0\fR statements. Note that currently \s-1ONLY\s0 these work for \fB\s-1VDEF\s0\fR. .IP "\s-1MAXIMUM\s0, \s-1MINIMUM\s0, \s-1AVERAGE\s0" 4 .IX Item "MAXIMUM, MINIMUM, AVERAGE" Return the corresponding value, \s-1MAXIMUM\s0 and \s-1MINIMUM\s0 also return the first occurrence of that value in the time component. .Sp Example: \f(CW\*(C`VDEF:avg=mydata,AVERAGE\*(C'\fR .IP "\s-1STDEV\s0" 4 .IX Item "STDEV" Returns the standard deviation of the values. .Sp Example: \f(CW\*(C`VDEF:stdev=mydata,STDEV\*(C'\fR .IP "\s-1LAST\s0, \s-1FIRST\s0" 4 .IX Item "LAST, FIRST" Return the last/first non-nan or infinite value for the selected data stream, including its timestamp. .Sp Example: \f(CW\*(C`VDEF:first=mydata,FIRST\*(C'\fR .IP "\s-1TOTAL\s0" 4 .IX Item "TOTAL" Returns the rate from each defined time slot multiplied with the step size. This can, for instance, return total bytes transferred when you have logged bytes per second. The time component returns the number of seconds. .Sp Example: \f(CW\*(C`VDEF:total=mydata,TOTAL\*(C'\fR .IP "\s-1PERCENT\s0, \s-1PERCENTNAN\s0" 4 .IX Item "PERCENT, PERCENTNAN" This should follow a \fB\s-1DEF\s0\fR or \fB\s-1CDEF\s0\fR \fIvname\fR. The \fIvname\fR is popped, another number is popped which is a certain percentage (0..100). The data set is then sorted and the value returned is chosen such that \&\fIpercentage\fR percent of the values is lower or equal than the result. For \s-1PERCENTNAN\s0 \fIUnknown\fR values are ignored, but for \s-1PERCENT\s0 \&\fIUnknown\fR values are considered lower than any finite number for this purpose so if this operator returns an \fIunknown\fR you have quite a lot of them in your data. \fBInf\fRinite numbers are lesser, or more, than the finite numbers and are always more than the \fIUnknown\fR numbers. (NaN < \-INF < finite values < \s-1INF\s0) .Sp Example: \f(CW\*(C`VDEF:perc95=mydata,95,PERCENT\*(C'\fR \f(CW\*(C`VDEF:percnan95=mydata,95,PERCENTNAN\*(C'\fR .IP "\s-1LSLSLOPE\s0, \s-1LSLINT\s0, \s-1LSLCORREL\s0" 4 .IX Item "LSLSLOPE, LSLINT, LSLCORREL" Return the parameters for a \fBL\fReast \fBS\fRquares \fBL\fRine \fI(y = mx +b)\fR which approximate the provided dataset. \s-1LSLSLOPE\s0 is the slope \fI(m)\fR of the line related to the \s-1COUNT\s0 position of the data. \s-1LSLINT\s0 is the y\-intercept \fI(b)\fR, which happens also to be the first data point on the graph. \s-1LSLCORREL\s0 is the Correlation Coefficient (also know as Pearson's Product Moment Correlation Coefficient). It will range from 0 to +/\-1 and represents the quality of fit for the approximation. .Sp Example: \f(CW\*(C`VDEF:slope=mydata,LSLSLOPE\*(C'\fR .SH "SEE ALSO" .IX Header "SEE ALSO" rrdgraph gives an overview of how \fBrrdtool graph\fR works. rrdgraph_data describes \fB\s-1DEF\s0\fR,\fB\s-1CDEF\s0\fR and \fB\s-1VDEF\s0\fR in detail. rrdgraph_rpn describes the \fB\s-1RPN\s0\fR language used in the \fB?DEF\fR statements. rrdgraph_graph page describes all of the graph and print functions. .PP Make sure to read rrdgraph_examples for tips&tricks. .SH "AUTHOR" .IX Header "AUTHOR" Program by Tobias Oetiker .PP This manual page by Alex van den Bogaerdt with corrections and/or additions by several people rrdtool-1.4.7/doc/rrdfirst.10000644000175300017510000001073211707501547012635 00000000000000.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.07) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "RRDFIRST 1" .TH RRDFIRST 1 "2008-03-15" "1.4.7" "rrdtool" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" rrdfirst \- Return the date of the first data sample in an RRA within an RRD .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBrrdtool\fR \fBfirst\fR \fIfilename\fR [\fI\-\-rraindex number\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" The \fBfirst\fR function returns the \s-1UNIX\s0 timestamp of the first data sample entered into the specified \s-1RRA\s0 of the \s-1RRD\s0 file. .IP "\fIfilename\fR" 8 .IX Item "filename" The name of the \fB\s-1RRD\s0\fR that contains the data. .IP "\fI\-\-rraindex number\fR" 8 .IX Item "--rraindex number" The index number of the \fB\s-1RRA\s0\fR that is to be examined. If not specified, the index defaults to zero. \fB\s-1RRA\s0\fR index numbers can be determined through \&\fBrrdtool info\fR. .SH "AUTHOR" .IX Header "AUTHOR" Burton Strauss rrdtool-1.4.7/doc/rpntutorial.10000644000175300017510000002656711707501546013375 00000000000000.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.07) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "RPNTUTORIAL 1" .TH RPNTUTORIAL 1 "2009-12-08" "1.4.7" "rrdtool" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" rpntutorial \- Reading RRDtool RPN Expressions by Steve Rader .SH "DESCRIPTION" .IX Header "DESCRIPTION" This tutorial should help you get to grips with RRDtool \s-1RPN\s0 expressions as seen in \s-1CDEF\s0 arguments of RRDtool graph. .SH "Reading Comparison Operators" .IX Header "Reading Comparison Operators" The \s-1LT\s0, \s-1LE\s0, \s-1GT\s0, \s-1GE\s0 and \s-1EQ\s0 \s-1RPN\s0 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, then replace the three values from the stack with \*(L"1\*(R". If the statement if false, replace the three values with \*(L"0\*(R". .PP For example, think about \*(L"2,1,GT\*(R". This \s-1RPN\s0 expression could be read as \*(L"is two greater than one?\*(R" The answer to that question is \&\*(L"true\*(R". So the three values should be replaced with \*(L"1\*(R". Thus the \&\s-1RPN\s0 expression 2,1,GT evaluates to 1. .PP Now consider \*(L"2,1,LE\*(R". This \s-1RPN\s0 expression could be read as \*(L"is two less than or equal to one?\*(R". The natural response is \*(L"no\*(R" and thus the \s-1RPN\s0 expression 2,1,LE evaluates to 0. .SH "Reading the IF Operator" .IX Header "Reading the IF Operator" The \s-1IF\s0 \s-1RPN\s0 logic operator can be straightforward also. The key to reading \s-1IF\s0 operators is to understand that the condition part of the traditional \*(L"if X than Y else Z\*(R" notation has *already* been evaluated. So the \s-1IF\s0 operator acts on only one value on the stack: the third value to the left of the \s-1IF\s0 value. The second value to the left of the \s-1IF\s0 corresponds to the true (\*(L"Y\*(R") branch. And the first value to the left of the \s-1IF\s0 corresponds to the false (\*(L"Z\*(R") branch. Read the \s-1RPN\s0 expression \*(L"X,Y,Z,IF\*(R" from left to right like so: \*(L"if X then Y else Z\*(R". .PP For example, consider \*(L"1,10,100,IF\*(R". It looks bizarre to me. But when I read \*(L"if 1 then 10 else 100\*(R" it's crystal clear: 1 is true so the answer is 10. Note that only zero is false; all other values are true. \*(L"2,20,200,IF\*(R" (\*(L"if 2 then 20 else 200\*(R") evaluates to 20. And \*(L"0,1,2,IF\*(R" ("if 0 then 1 else 2) evaluates to 2. .PP Notice that none of the above examples really simulate the whole \&\*(L"if X then Y else Z\*(R" statement. This is because computer programmers read this statement as \*(L"if Some Condition then Y else Z\*(R". So it's important to be able to read \s-1IF\s0 operators along with the \s-1LT\s0, \s-1LE\s0, \&\s-1GT\s0, \s-1GE\s0 and \s-1EQ\s0 operators. .SH "Some Examples" .IX Header "Some Examples" While compound expressions can look overly complex, they can be considered elegantly simple. To quickly comprehend \s-1RPN\s0 expressions, you must know the algorithm for evaluating \s-1RPN\s0 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. .PP For example, the stack \*(L"1,2,3,+,+\*(R" gets \*(L"2,3,+\*(R" evaluated (as \*(L"2+3\*(R") during the first iteration and is replaced by 5. This results in the stack \*(L"1,5,+\*(R". Finally, \*(L"1,5,+\*(R" is evaluated resulting in the answer 6. For convenience, it's useful to write this set of operations as: .PP .Vb 3 \& 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 .Ve .PP Let's use that notation to conveniently solve some complex \s-1RPN\s0 expressions with multiple logic operators: .PP .Vb 1 \& 1) 20,10,GT,10,20,IF eval is 20,10,GT = 1 result is 1,10,20,IF .Ve .PP read the eval as pop \*(L"20 is greater than 10\*(R" so push 1 .PP .Vb 1 \& 2) 1,10,20,IF eval is 1,10,20,IF = 10 result is 10 .Ve .PP read pop \*(L"if 1 then 10 else 20\*(R" so push 10. Only 10 is left so 10 is the answer. .PP Let's read a complex \s-1RPN\s0 expression that also has the traditional multiplication operator: .PP .Vb 4 \& 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, 7000,128,8,*,IF eval 128,8,* result is 1024 \& 4) 0, 7000,1024, IF result is 1024 .Ve .PP Now let's go back to the first example of multiple logic operators, but replace the value 20 with the variable \*(L"input\*(R": .PP .Vb 1 \& 1) input,10,GT,10,input,IF eval is input,10,GT ( lets call this A ) .Ve .PP Read eval as \*(L"if input > 10 then true\*(R" and replace \*(L"input,10,GT\*(R" with \*(L"A\*(R": .PP .Vb 1 \& 2) A,10,input,IF eval is A,10,input,IF .Ve .PP read \*(L"if A then 10 else input\*(R". Now replace A with it's verbose description again and\*(--voila!\-\-you have an easily readable description of the expression: .PP .Vb 1 \& if input > 10 then 10 else input .Ve .PP Finally, let's go back to the first most complex example and replace the value 128 with \*(L"input\*(R": .PP .Vb 1 \& 1) input,8,*,7000,GT,7000,input,8,*,IF eval input,8,* result is A .Ve .PP where A is \*(L"input * 8\*(R" .PP .Vb 1 \& 2) A,7000,GT,7000,input,8,*,IF eval is A,7000,GT result is B .Ve .PP where B is \*(L"if ((input * 8) > 7000) then true\*(R" .PP .Vb 1 \& 3) B,7000,input,8,*,IF eval is input,8,* result is C .Ve .PP where C is \*(L"input * 8\*(R" .PP .Vb 1 \& 4) B,7000,C,IF .Ve .PP At last we have a readable decoding of the complex \s-1RPN\s0 expression with a variable: .PP .Vb 1 \& if ((input * 8) > 7000) then 7000 else (input * 8) .Ve .SH "Exercises" .IX Header "Exercises" Exercise 1: .PP Compute \*(L"3,2,*,1,+ and \*(R"3,2,1,+,*" by hand. Rewrite them in traditional notation. Explain why they have different answers. .PP Answer 1: .PP .Vb 3 \& 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. .Ve .PP Exercise 2: .PP One may be tempted to shorten the expression .PP .Vb 1 \& input,8,*,56000,GT,56000,input,*,8,IF .Ve .PP by removing the redundant use of \*(L"input,8,*\*(R" like so: .PP .Vb 1 \& input,56000,GT,56000,input,IF,8,* .Ve .PP Use traditional notation to show these expressions are not the same. Write an expression that's equivalent to the first expression, but uses the \s-1LE\s0 and \s-1DIV\s0 operators. .PP Answer 2: .PP .Vb 2 \& if (input <= 56000/8 ) { input*8 } else { 56000 } \& input,56000,8,DIV,LT,input,8,*,56000,IF .Ve .PP Exercise 3: .PP Briefly explain why traditional mathematic notation requires the use of parentheses. Explain why \s-1RPN\s0 notation does not require the use of parentheses. .PP Answer 3: .PP .Vb 6 \& Traditional mathematic expressions are evaluated by \& doing multiplication and division first, then addition and \& subtraction. Parentheses 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. .Ve .PP Exercise 4: .PP Explain why it was desirable for the RRDtool developers to implement \&\s-1RPN\s0 notation instead of traditional mathematical notation. .PP Answer 4: .PP .Vb 5 \& 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.) .Ve .SH "AUTHOR" .IX Header "AUTHOR" Steve Rader rrdtool-1.4.7/doc/rrdxport.10000644000175300017510000002416511707501546012666 00000000000000.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.07) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "RRDXPORT 1" .TH RRDXPORT 1 "2012-01-16" "1.4.7" "rrdtool" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" rrdxport \- Export data in XML format based on data from one or several RRD .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBrrdtool\fR \fBxport\fR [\fB\-s\fR|\fB\-\-start\fR\ \fIseconds\fR] [\fB\-e\fR|\fB\-\-end\fR\ \fIseconds\fR] [\fB\-m\fR|\fB\-\-maxrows\fR\ \fIrows\fR] [\fB\-\-step\fR\ \fIvalue\fR] [\fB\-\-json\fR] [\fB\-\-daemon\fR\ \fIaddress\fR] [\fB\s-1DEF:\s0\fR\fIvname\fR\fB=\fR\fIrrd\fR\fB:\fR\fIds-name\fR\fB:\fR\fI\s-1CF\s0\fR] [\fB\s-1CDEF:\s0\fR\fIvname\fR\fB=\fR\fIrpn-expression\fR] [\fB\s-1XPORT\s0\fR\fB:\fR\fIvname\fR[\fB:\fR\fIlegend\fR]] .SH "DESCRIPTION" .IX Header "DESCRIPTION" The \fBxport\fR function's main purpose is to write an \s-1XML\s0 formatted representation of the data stored in one or several \fB\s-1RRD\s0\fRs. It can also extract numerical reports. .PP If no \fI\s-1XPORT\s0\fR statements are found, there will be no output. .IP "\fB\-s\fR|\fB\-\-start\fR \fIseconds\fR (default end\-1day)" 4 .IX Item "-s|--start seconds (default end-1day)" The time when the exported range should begin. 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 printed. See also AT-STYLE \s-1TIME\s0 \s-1SPECIFICATION\s0 section in the \fIrrdfetch\fR documentation for a detailed explanation on how to specify time. .IP "\fB\-e\fR|\fB\-\-end\fR \fIseconds\fR (default now)" 4 .IX Item "-e|--end seconds (default now)" The time when the exported range should end. Time in seconds since epoch. See also AT-STYLE \s-1TIME\s0 \s-1SPECIFICATION\s0 section in the \fIrrdfetch\fR documentation for a detailed explanation of ways to specify time. .IP "\fB\-m\fR|\fB\-\-maxrows\fR \fIrows\fR (default 400 rows)" 4 .IX Item "-m|--maxrows rows (default 400 rows)" This works like the \fB\-w\fR|\fB\-\-width\fR parameter of \fIrrdgraph\fR. In fact it is exactly the same, but the parameter was renamed to describe its purpose in this module. See \fIrrdgraph\fR documentation for details. .IP "\fB\-\-step\fR \fIvalue\fR (default automatic)" 4 .IX Item "--step value (default automatic)" See rrdgraph documentation. .IP "\fB\-\-daemon\fR \fIaddress\fR" 4 .IX Item "--daemon address" Address of the rrdcached daemon. If specified, a \f(CW\*(C`flush\*(C'\fR command is sent to the server before reading the \s-1RRD\s0 files. This allows \fBrrdtool\fR to return fresh data even if the daemon is configured to cache values for a long time. For a list of accepted formats, see the \fB\-l\fR option in the rrdcached manual. .Sp .Vb 1 \& rrdtool xport \-\-daemon unix:/var/run/rrdcached.sock ... .Ve .IP "\fB\-\-json\fR" 4 .IX Item "--json" produce json formated output (instead of xml) .IP "\fB\-\-enumds\fR" 4 .IX Item "--enumds" The generated xml should contain the data values in enumerated tags. .Sp .Vb 1 \& valval .Ve .IP "\fB\s-1DEF:\s0\fR\fIvname\fR\fB=\fR\fIrrd\fR\fB:\fR\fIds-name\fR\fB:\fR\fI\s-1CF\s0\fR" 4 .IX Item "DEF:vname=rrd:ds-name:CF" See \fIrrdgraph\fR documentation. .IP "\fB\s-1CDEF:\s0\fR\fIvname\fR\fB=\fR\fIrpn-expression\fR" 4 .IX Item "CDEF:vname=rpn-expression" See \fIrrdgraph\fR documentation. .IP "\fB\s-1XPORT:\s0\fR\fIvname\fR\fB:\fR\fB:\fR\fIlegend\fR" 4 .IX Item "XPORT:vname::legend" At least one \fI\s-1XPORT\s0\fR statement should be present. The values referenced by \fIvname\fR are printed. Optionally add a legend. .SH "Output format" .IX Header "Output format" The output is enclosed in an \fBxport\fR element and contains two blocks. The first block is enclosed by a \fBmeta\fR element and contains some meta data. The second block is enclosed by a \&\fBdata\fR element and contains the data rows. .PP Let's assume that the \fIxport\fR command looks like this: .PP .Vb 7 \& rrdtool xport \e \& \-\-start now\-1h \-\-end now \e \& DEF:xx=host\-inout.lo.rrd:output:AVERAGE \e \& DEF:yy=host\-inout.lo.rrd:input:AVERAGE \e \& CDEF:aa=xx,yy,+,8,* \e \& XPORT:xx:"out bytes" \e \& XPORT:aa:"in and out bits" .Ve .PP The resulting meta data section is (the values will depend on the \&\s-1RRD\s0 characteristics): .PP .Vb 11 \& \& 1020611700 \& 300 \& 1020615600 \& 14 \& 2 \& \& out bytes \& in and out bits \& \& .Ve .PP The resulting data section is: .PP .Vb 10 \& \& 10206117003.4000000000e+005.4400000000e+01 \& 10206120003.4000000000e+005.4400000000e+01 \& 10206123003.4000000000e+005.4400000000e+01 \& 10206126003.4113333333e+005.4581333333e+01 \& 10206129003.4000000000e+005.4400000000e+01 \& 10206132003.4000000000e+005.4400000000e+01 \& 10206135003.4000000000e+005.4400000000e+01 \& 10206138003.4000000000e+005.4400000000e+01 \& 10206141003.4000000000e+005.4400000000e+01 \& 10206144003.4000000000e+005.4400000000e+01 \& 10206147003.7333333333e+005.9733333333e+01 \& 10206150003.4000000000e+005.4400000000e+01 \& 10206153003.4000000000e+005.4400000000e+01 \& 1020615600NaNNaN \& .Ve .SH "EXAMPLE 1" .IX Header "EXAMPLE 1" .Vb 3 \& rrdtool xport \e \& DEF:out=if1\-inouts.rrd:outoctets:AVERAGE \e \& XPORT:out:"out bytes" .Ve .SH "EXAMPLE 2" .IX Header "EXAMPLE 2" .Vb 7 \& rrdtool xport \e \& DEF:out1=if1\-inouts.rrd:outoctets:AVERAGE \e \& DEF:out2=if2\-inouts.rrd:outoctets:AVERAGE \e \& CDEF:sum=out1,out2,+ \e \& XPORT:out1:"if1 out bytes" \e \& XPORT:out2:"if2 out bytes" \e \& XPORT:sum:"output sum" .Ve .SH "ENVIRONMENT VARIABLES" .IX Header "ENVIRONMENT VARIABLES" The following environment variables may be used to change the behavior of \&\f(CW\*(C`rrdtool\ xport\*(C'\fR: .IP "\fB\s-1RRDCACHED_ADDRESS\s0\fR" 4 .IX Item "RRDCACHED_ADDRESS" If this environment variable is set it will have the same effect as specifying the \f(CW\*(C`\-\-daemon\*(C'\fR option on the command line. If both are present, the command line argument takes precedence. .SH "AUTHOR" .IX Header "AUTHOR" Tobias Oetiker rrdtool-1.4.7/doc/rrdthreads.10000644000175300017510000002040311707501546013133 00000000000000.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.07) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "RRDTHREADS 1" .TH RRDTHREADS 1 "2008-06-08" "1.4.7" "rrdtool" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" rrdthreads \- Provisions for linking the RRD library to use in multi\-threaded programs .SH "SYNOPSIS" .IX Header "SYNOPSIS" Using librrd in multi-threaded programs requires some extra precautions, as the \s-1RRD\s0 library in its original form was not thread-safe at all. This document describes requirements and pitfalls on the way to use the multi-threaded version of librrd in your own programs. It also gives hints for future \s-1RRD\s0 development to keep the library thread-safe. .PP Currently only some \s-1RRD\s0 operations are implemented in a thread-safe way. They all end in the usual "\f(CW\*(C`_r\*(C'\fR" suffix. .SH "DESCRIPTION" .IX Header "DESCRIPTION" In order to use librrd in multi-threaded programs you must: .IP "\(bu" 4 Link with \fIlibrrd_th\fR instead of \fIlibrrd\fR (use \f(CW\*(C`\-lrrd_th\*(C'\fR when linking) .IP "\(bu" 4 Use the "\f(CW\*(C`_r\*(C'\fR" functions instead of the normal API-functions .IP "\(bu" 4 Do not use any at-style time specifications. Parsing of such time specifications is terribly non-thread-safe. .IP "\(bu" 4 Never use non *\f(CW\*(C`_r\*(C'\fR functions unless it is explicitly documented that the function is tread-safe. .IP "\(bu" 4 Every thread \s-1SHOULD\s0 call \f(CW\*(C`rrd_get_context()\*(C'\fR before its first call to any \f(CW\*(C`librrd_th\*(C'\fR function in order to set up thread specific data. This is not strictly required, but it is the only way to test if memory allocation can be done by this function. Otherwise the program may die with a \s-1SIGSEGV\s0 in a low-memory situation. .IP "\(bu" 4 Always call \f(CW\*(C`rrd_error_clear()\*(C'\fR before any call to the library. Otherwise the call might fail due to some earlier error. .SS "\s-1NOTES\s0 \s-1FOR\s0 \s-1RRD\s0 \s-1CONTRIBUTORS\s0" .IX Subsection "NOTES FOR RRD CONTRIBUTORS" Some precautions must be followed when developing \s-1RRD\s0 from now on: .IP "\(bu" 4 Only use thread-safe functions in library code. Many often used libc functions aren't thread-safe. Take care in the following situations or when using the following library functions: .RS 4 .IP "\(bu" 4 Direct calls to \f(CW\*(C`strerror()\*(C'\fR must be avoided: use \f(CW\*(C`rrd_strerror()\*(C'\fR instead, it provides a per-thread error message. .IP "\(bu" 4 The \f(CW\*(C`getpw*\*(C'\fR, \f(CW\*(C`getgr*\*(C'\fR, \f(CW\*(C`gethost*\*(C'\fR function families (and some more \&\f(CW\*(C`get*\*(C'\fR functions) are not thread-safe: use the *\f(CW\*(C`_r\*(C'\fR variants .IP "\(bu" 4 Time functions: \f(CW\*(C`asctime\*(C'\fR, \f(CW\*(C`ctime\*(C'\fR, \f(CW\*(C`gmtime\*(C'\fR, \f(CW\*(C`localtime\*(C'\fR: use *\f(CW\*(C`_r\*(C'\fR variants .IP "\(bu" 4 \&\f(CW\*(C`strtok\*(C'\fR: use \f(CW\*(C`strtok_r\*(C'\fR .IP "\(bu" 4 \&\f(CW\*(C`tmpnam\*(C'\fR: use \f(CW\*(C`tmpnam_r\*(C'\fR .IP "\(bu" 4 Many others (lookup documentation) .RE .RS 4 .RE .IP "\(bu" 4 A header file named \fIrrd_is_thread_safe.h\fR is provided that works with the \s-1GNU\s0 C\-preprocessor to \*(L"poison\*(R" some of the most common non-thread-safe functions using the \f(CW\*(C`#pragma GCC poison\*(C'\fR directive. Just include this header in source files you want to keep thread-safe. .IP "\(bu" 4 Do not introduce global variables! .Sp If you really, really have to use a global variable you may add a new field to the \f(CW\*(C`rrd_context\*(C'\fR structure and modify \fIrrd_error.c\fR, \&\fIrrd_thread_safe.c\fR and \fIrrd_non_thread_safe.c\fR .IP "\(bu" 4 Do not use \f(CW\*(C`getopt\*(C'\fR or \f(CW\*(C`getopt_long\*(C'\fR in *\f(CW\*(C`_r\*(C'\fR (neither directly nor indirectly). .Sp \&\f(CW\*(C`getopt\*(C'\fR uses global variables and behaves badly in a multi-threaded application when called concurrently. Instead provide a *_r function taking all options as function parameters. You may provide argc and **argv arguments for variable length argument lists. See \&\f(CW\*(C`rrd_update_r\*(C'\fR as an example. .IP "\(bu" 4 Do not use the \f(CW\*(C`rrd_parsetime\*(C'\fR function! .Sp It uses lots of global variables. You may use it in functions not designed to be thread-safe, like in functions wrapping the \f(CW\*(C`_r\*(C'\fR version of some operation (e.g., \f(CW\*(C`rrd_create\*(C'\fR, but not in \f(CW\*(C`rrd_create_r\*(C'\fR) .SS "\s-1CURRENTLY\s0 \s-1IMPLEMENTED\s0 \s-1THREAD\s0 \s-1SAFE\s0 \s-1FUNCTIONS\s0" .IX Subsection "CURRENTLY IMPLEMENTED THREAD SAFE FUNCTIONS" Currently there exist thread-safe variants of \f(CW\*(C`rrd_update\*(C'\fR, \&\f(CW\*(C`rrd_create\*(C'\fR, \f(CW\*(C`rrd_dump\*(C'\fR, \f(CW\*(C`rrd_info\*(C'\fR, \f(CW\*(C`rrd_last\*(C'\fR, and \f(CW\*(C`rrd_fetch\*(C'\fR. .SH "AUTHOR" .IX Header "AUTHOR" Peter Stamfest rrdtool-1.4.7/doc/rrdgraph_graph.10000644000175300017510000006042211707501546013770 00000000000000.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.07) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "RRDGRAPH_GRAPH 1" .TH RRDGRAPH_GRAPH 1 "2012-01-20" "1.4.7" "rrdtool" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" rrdgraph_graph \- rrdtool graph command reference .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fB\s-1PRINT\s0\fR\fB:\fR\fIvname\fR\fB:\fR\fIformat\fR .PP \&\fB\s-1GPRINT\s0\fR\fB:\fR\fIvname\fR\fB:\fR\fIformat\fR .PP \&\fB\s-1COMMENT\s0\fR\fB:\fR\fItext\fR .PP \&\fB\s-1VRULE\s0\fR\fB:\fR\fItime\fR\fB#\fR\fIcolor\fR[\fB:\fR\fIlegend\fR][\fB:dashes\fR[\fB=\fR\fIon_s\fR[,\fIoff_s\fR[,\fIon_s\fR,\fIoff_s\fR]...]][\fB:dash\-offset=\fR\fIoffset\fR]] .PP \&\fB\s-1HRULE\s0\fR\fB:\fR\fIvalue\fR\fB#\fR\fIcolor\fR[\fB:\fR\fIlegend\fR][\fB:dashes\fR[\fB=\fR\fIon_s\fR[,\fIoff_s\fR[,\fIon_s\fR,\fIoff_s\fR]...]][\fB:dash\-offset=\fR\fIoffset\fR]] .PP \&\fB\s-1LINE\s0\fR[\fIwidth\fR]\fB:\fR\fIvalue\fR[\fB#\fR\fIcolor\fR][\fB:\fR[\fIlegend\fR][\fB:STACK\fR]][\fB:dashes\fR[\fB=\fR\fIon_s\fR[,\fIoff_s\fR[,\fIon_s\fR,\fIoff_s\fR]...]][\fB:dash\-offset=\fR\fIoffset\fR]] .PP \&\fB\s-1AREA\s0\fR\fB:\fR\fIvalue\fR[\fB#\fR\fIcolor\fR][\fB:\fR[\fIlegend\fR][\fB:STACK\fR]] .PP \&\fB\s-1TICK\s0\fR\fB:\fR\fIvname\fR\fB#\fR\fIrrggbb\fR[\fIaa\fR][\fB:\fR\fIfraction\fR[\fB:\fR\fIlegend\fR]] .PP \&\fB\s-1SHIFT\s0\fR\fB:\fR\fIvname\fR\fB:\fR\fIoffset\fR .PP \&\fB\s-1TEXTALIGN\s0\fR\fB:\fR{\fBleft\fR|\fBright\fR|\fBjustified\fR|\fBcenter\fR} .PP \&\fB\s-1PRINT\s0\fR\fB:\fR\fIvname\fR\fB:\fR\fI\s-1CF\s0\fR\fB:\fR\fIformat\fR (deprecated) .PP \&\fB\s-1GPRINT\s0\fR\fB:\fR\fIvname\fR\fB:\fR\fI\s-1CF\s0\fR\fB:\fR\fIformat\fR (deprecated) .PP \&\fB\s-1STACK\s0\fR\fB:\fR\fIvname\fR\fB#\fR\fIcolor\fR[\fB:\fR\fIlegend\fR] (deprecated) .SH "DESCRIPTION" .IX Header "DESCRIPTION" These instructions allow you to generate your image or report. If you don't use any graph elements, no graph is generated. Similarly, no report is generated if you don't use print options. .SS "\s-1PRINT\s0" .IX Subsection "PRINT" \fI\f(BI\s-1PRINT:\s0\fI\fIvname\fI\f(BI:\fI\fIformat\fI[\f(BI:strftime\fI]\fR .IX Subsection "PRINT:vname:format[:strftime]" .PP Depending on the context, either the value component or the time component of a \fB\s-1VDEF\s0\fR is printed using \fIformat\fR. It is an error to specify a \fIvname\fR generated by a \fB\s-1DEF\s0\fR or \fB\s-1CDEF\s0\fR. .PP Any text in \fIformat\fR is printed literally with one exception: The percent character introduces a formatter string. This string can be: .PP For printing values: .IP "\fB%%\fR" 4 .IX Item "%%" just prints a literal '%' character .IP "\fB%#.#le\fR" 4 .IX Item "%#.#le" prints numbers like 1.2346e+04. The optional integers # denote field width and decimal precision. .IP "\fB%#.#lf\fR" 4 .IX Item "%#.#lf" prints numbers like 12345.6789, with optional field width and precision. .ie n .IP "\fB\fB%s\fB\fR" 4 .el .IP "\fB\f(CB%s\fB\fR" 4 .IX Item "%s" place this after \fB\f(CB%le\fB\fR, \fB\f(CB%lf\fB\fR or \fB\f(CB%lg\fB\fR. This will be replaced by the appropriate \s-1SI\s0 magnitude unit and the value will be scaled accordingly (123456 \-> 123.456 k). .ie n .IP "\fB\fB%S\fB\fR" 4 .el .IP "\fB\f(CB%S\fB\fR" 4 .IX Item "%S" is similar to \fB\f(CB%s\fB\fR. It does, however, use a previously defined magnitude unit. If there is no such unit yet, it tries to define one (just like \fB\f(CB%s\fB\fR) unless the value is zero, in which case the magnitude unit stays undefined. Thus, formatter strings using \fB\f(CB%S\fB\fR and no \fB\f(CB%s\fB\fR will all use the same magnitude unit except for zero values. .PP If you \s-1PRINT\s0 a \s-1VDEF\s0 value, you can also print the time associated with it by appending the string \&\fB:strftime\fR to the format. Note that RRDtool uses the strftime function of your OSs C library. This means that the conversion specifier may vary. Check the manual page if you are uncertain. The following is a list of conversion specifiers usually supported across the board. .ie n .IP "\fB\fB%a\fB\fR" 4 .el .IP "\fB\f(CB%a\fB\fR" 4 .IX Item "%a" The abbreviated weekday name according to the current locale. .ie n .IP "\fB\fB%A\fB\fR" 4 .el .IP "\fB\f(CB%A\fB\fR" 4 .IX Item "%A" The full weekday name according to the current locale. .ie n .IP "\fB\fB%b\fB\fR" 4 .el .IP "\fB\f(CB%b\fB\fR" 4 .IX Item "%b" The abbreviated month name according to the current locale. .ie n .IP "\fB\fB%B\fB\fR" 4 .el .IP "\fB\f(CB%B\fB\fR" 4 .IX Item "%B" The full month name according to the current locale. .ie n .IP "\fB\fB%c\fB\fR" 4 .el .IP "\fB\f(CB%c\fB\fR" 4 .IX Item "%c" The preferred date and time representation for the current locale. .ie n .IP "\fB\fB%d\fB\fR" 4 .el .IP "\fB\f(CB%d\fB\fR" 4 .IX Item "%d" The day of the month as a decimal number (range 01 to 31). .ie n .IP "\fB\fB%H\fB\fR" 4 .el .IP "\fB\f(CB%H\fB\fR" 4 .IX Item "%H" The hour as a decimal number using a 24\-hour clock (range 00 to 23). .ie n .IP "\fB\fB%I\fB\fR" 4 .el .IP "\fB\f(CB%I\fB\fR" 4 .IX Item "%I" The hour as a decimal number using a 12\-hour clock (range 01 to 12). .ie n .IP "\fB\fB%j\fB\fR" 4 .el .IP "\fB\f(CB%j\fB\fR" 4 .IX Item "%j" The day of the year as a decimal number (range 001 to 366). .ie n .IP "\fB\fB%m\fB\fR" 4 .el .IP "\fB\f(CB%m\fB\fR" 4 .IX Item "%m" The month as a decimal number (range 01 to 12). .ie n .IP "\fB\fB%M\fB\fR" 4 .el .IP "\fB\f(CB%M\fB\fR" 4 .IX Item "%M" The minute as a decimal number (range 00 to 59). .ie n .IP "\fB\fB%p\fB\fR" 4 .el .IP "\fB\f(CB%p\fB\fR" 4 .IX Item "%p" Either `\s-1AM\s0' or `\s-1PM\s0' according to the given time value, or the corresponding strings for the current locale. Noon is treated as `pm' and midnight as `am'. Note that in many locales and `pm' notation is unsupported and in such cases \f(CW%p\fR will return an empty string. .ie n .IP "\fB\fB%s\fB\fR" 4 .el .IP "\fB\f(CB%s\fB\fR" 4 .IX Item "%s" The second as a decimal number (range 00 to 61). .ie n .IP "\fB\fB%S\fB\fR" 4 .el .IP "\fB\f(CB%S\fB\fR" 4 .IX Item "%S" The seconds since the epoch (1.1.1970) (libc dependent non standard!) .ie n .IP "\fB\fB%U\fB\fR" 4 .el .IP "\fB\f(CB%U\fB\fR" 4 .IX Item "%U" The week number of the current year as a decimal number, range 00 to 53, starting with the first Sunday as the first day of week 01. See also \f(CW%V\fR and \f(CW%W\fR. .ie n .IP "\fB\fB%V\fB\fR" 4 .el .IP "\fB\f(CB%V\fB\fR" 4 .IX Item "%V" The \s-1ISO\s0 8601:1988 week number of the current year as a decimal number, range 01 to 53, where week 1 is the first week that has at least 4 days in the current year, and with Monday as the first day of the week. See also \f(CW%U\fR and \f(CW%W\fR. .ie n .IP "\fB\fB%w\fB\fR" 4 .el .IP "\fB\f(CB%w\fB\fR" 4 .IX Item "%w" The day of the week as a decimal, range 0 to 6, Sunday being 0. See also \f(CW%u\fR. .ie n .IP "\fB\fB%W\fB\fR" 4 .el .IP "\fB\f(CB%W\fB\fR" 4 .IX Item "%W" The week number of the current year as a decimal number, range 00 to 53, starting with the first Monday as the first day of week 01. .ie n .IP "\fB\fB%x\fB\fR" 4 .el .IP "\fB\f(CB%x\fB\fR" 4 .IX Item "%x" The preferred date representation for the current locale without the time. .ie n .IP "\fB\fB%X\fB\fR" 4 .el .IP "\fB\f(CB%X\fB\fR" 4 .IX Item "%X" The preferred time representation for the current locale without the date. .ie n .IP "\fB\fB%y\fB\fR" 4 .el .IP "\fB\f(CB%y\fB\fR" 4 .IX Item "%y" The year as a decimal number without a century (range 00 to 99). .ie n .IP "\fB\fB%Y\fB\fR" 4 .el .IP "\fB\f(CB%Y\fB\fR" 4 .IX Item "%Y" The year as a decimal number including the century. .ie n .IP "\fB\fB%Z\fB\fR" 4 .el .IP "\fB\f(CB%Z\fB\fR" 4 .IX Item "%Z" The time zone or name or abbreviation. .IP "\fB%%\fR" 4 .IX Item "%%" A literal `%' character. .PP \fI\f(BI\s-1PRINT:\s0\fI\fIvname\fI\f(BI:\fI\fI\s-1CF\s0\fI\f(BI:\fI\fIformat\fI\fR .IX Subsection "PRINT:vname:CF:format" .PP \&\fIDeprecated. Use the new form of this command in new scripts.\fR The first form of this command is to be used with \fB\s-1CDEF\s0\fR \fIvname\fRs. .SS "\s-1GRAPH\s0" .IX Subsection "GRAPH" \fI\f(BI\s-1GPRINT\s0\fI\f(BI:\fI\fIvname\fI\f(BI:\fI\fIformat\fI\fR .IX Subsection "GPRINT:vname:format" .PP This is the same as \f(CW\*(C`PRINT\*(C'\fR, but printed inside the graph. .PP \fI\f(BI\s-1GPRINT\s0\fI\f(BI:\fI\fIvname\fI\f(BI:\fI\fI\s-1CF\s0\fI\f(BI:\fI\fIformat\fI\fR .IX Subsection "GPRINT:vname:CF:format" .PP \&\fIDeprecated. Use the new form of this command in new scripts.\fR This is the same as \f(CW\*(C`PRINT\*(C'\fR, but printed inside the graph. .PP \fI\f(BI\s-1COMMENT\s0\fI\f(BI:\fI\fItext\fI\fR .IX Subsection "COMMENT:text" .PP Text is printed literally in the legend section of the graph. Note that in RRDtool 1.2 you have to escape colons in \s-1COMMENT\s0 text in the same way you have to escape them in \fB*PRINT\fR commands by writing \fB'\e:'\fR. .PP \fI\f(BI\s-1VRULE\s0\fI\f(BI:\fI\fItime\fI\f(BI#\fI\fIcolor\fI[\f(BI:\fI\fIlegend\fI][\f(BI:dashes\fI[\f(BI=\fI\fIon_s\fI[,\fIoff_s\fI[,\fIon_s\fI,\fIoff_s\fI]...]][\f(BI:dash\-offset=\fI\fIoffset\fI]]\fR .IX Subsection "VRULE:time#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]]" .PP Draw a vertical line at \fItime\fR. Its color is composed from three hexadecimal numbers specifying the rgb color components (00 is off, \s-1FF\s0 is maximum) red, green and blue followed by an optional alpha. Optionally, a legend box and string is printed in the legend section. \fItime\fR may be a number or a variable from a \fB\s-1VDEF\s0\fR. It is an error to use \fIvname\fRs from \fB\s-1DEF\s0\fR or \fB\s-1CDEF\s0\fR here. Dashed lines can be drawn using the \fBdashes\fR modifier. See \fB\s-1LINE\s0\fR for more details. .PP \fI\f(BI\s-1HRULE\s0\fI\f(BI:\fI\fIvalue\fI\f(BI#\fI\fIcolor\fI[\f(BI:\fI\fIlegend\fI][\f(BI:dashes\fI[\f(BI=\fI\fIon_s\fI[,\fIoff_s\fI[,\fIon_s\fI,\fIoff_s\fI]...]][\f(BI:dash\-offset=\fI\fIoffset\fI]]\fR .IX Subsection "HRULE:value#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]]" .PP Draw a horizontal line at \fIvalue\fR. \s-1HRULE\s0 acts much like \s-1LINE\s0 except that will have no effect on the scale of the graph. If a \s-1HRULE\s0 is outside the graphing area it will just not be visible. .PP \fI\f(BI\s-1LINE\s0\fI[\fIwidth\fI]\f(BI:\fI\fIvalue\fI[\f(BI#\fI\fIcolor\fI][\f(BI:\fI[\fIlegend\fI][\f(BI:STACK\fI]][\f(BI:dashes\fI[\f(BI=\fI\fIon_s\fI[,\fIoff_s\fI[,\fIon_s\fI,\fIoff_s\fI]...]][\f(BI:dash\-offset=\fI\fIoffset\fI]]\fR .IX Subsection "LINE[width]:value[#color][:[legend][:STACK]][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]]" .PP Draw a line of the specified width onto the graph. \fIwidth\fR can be a floating point number. If the color is not specified, the drawing is done \&'invisibly'. This is useful when stacking something else on top of this line. Also optional is the legend box and string which will be printed in the legend section if specified. The \fBvalue\fR can be generated by \fB\s-1DEF\s0\fR, \&\fB\s-1VDEF\s0\fR, and \fB\s-1CDEF\s0\fR. If the optional \fB\s-1STACK\s0\fR modifier is used, this line is stacked on top of the previous element which can be a \fB\s-1LINE\s0\fR or an \&\fB\s-1AREA\s0\fR. .PP The \fBdashes\fR modifier enables dashed line style. Without any further options a symmetric dashed line with a segment length of 5 pixels will be drawn. The dash pattern can be changed if the \fBdashes=\fR parameter is followed by either one value or an even number (1, 2, 4, 6, ...) of positive values. Each value provides the length of alternate \fIon_s\fR and \fIoff_s\fR portions of the stroke. The \fBdash-offset\fR parameter specifies an \fIoffset\fR into the pattern at which the stroke begins. .PP When you do not specify a color, you cannot specify a legend. Should you want to use \s-1STACK\s0, use the \*(L"LINEx:::STACK\*(R" form. .PP \fI\f(BI\s-1AREA\s0\fI\f(BI:\fI\fIvalue\fI[\f(BI#\fI\fIcolor\fI][\f(BI:\fI[\fIlegend\fI][\f(BI:STACK\fI]]\fR .IX Subsection "AREA:value[#color][:[legend][:STACK]]" .PP See \fB\s-1LINE\s0\fR, however the area between the x\-axis and the line will be filled. .PP \fI\f(BI\s-1TICK\s0\fI\f(BI:\fI\fIvname\fI\f(BI#\fI\fIrrggbb\fI[\fIaa\fI][\f(BI:\fI\fIfraction\fI[\f(BI:\fI\fIlegend\fI]]\fR .IX Subsection "TICK:vname#rrggbb[aa][:fraction[:legend]]" .PP Plot a tick mark (a vertical line) for each value of \fIvname\fR that is non-zero and not *UNKNOWN*. The \fIfraction\fR argument specifies the length of the tick mark as a fraction of the y\-axis; the default value is 0.1 (10% of the axis). Note that the color specification is not optional. The \s-1TICK\s0 marks normally start at the lower edge of the graphing area. If the fraction is negative they start at the upper border of the graphing area. .PP \fI\f(BI\s-1SHIFT\s0\fI\f(BI:\fI\fIvname\fI\f(BI:\fI\fIoffset\fI\fR .IX Subsection "SHIFT:vname:offset" .PP Using this command \fBRRDtool\fR will graph the following elements with the specified offset. For instance, you can specify an offset of (\ 7*24*60*60\ =\ )\ 604'800\ seconds to \*(L"look back\*(R" one week. Make sure to tell the viewer of your graph you did this ... As with the other graphing elements, you can specify a number or a variable here. .PP \fI\f(BI\s-1TEXTALIGN\s0\fI\f(BI:\fI{\f(BIleft\fI|\f(BIright\fI|\f(BIjustified\fI|\f(BIcenter\fI}\fR .IX Subsection "TEXTALIGN:{left|right|justified|center}" .PP Labels are placed below the graph. When they overflow to the left, they wrap to the next line. By default, lines are justified left and right. The \&\fB\s-1TEXTALIGN\s0\fR function lets you change this default. This is a command and not an option, so that you can change the default several times in your argument list. .PP \fI\f(BI\s-1STACK\s0\fI\f(BI:\fI\fIvname\fI\f(BI#\fI\fIcolor\fI[\f(BI:\fI\fIlegend\fI]\fR .IX Subsection "STACK:vname#color[:legend]" .PP \&\fIDeprecated. Use the \f(BI\s-1STACK\s0\fI modifiers on the other commands instead!\fR .PP \&\fBSome notes on stacking\fR .PP When stacking, an element is not placed above the X\-axis but rather on top of the previous element. There must be something to stack upon. .PP You can use an \fBinvisible\fR \s-1LINE\s0 or \s-1AREA\s0 to stacked upon. .PP An \fBunknown\fR value makes the entire stack unknown from that moment on. You don't know where to begin (the unknown value) and therefore do not know where to end. .PP If you want to make sure you will be displaying a certain variable, make sure never to stack upon the unknown value. Use a \s-1CDEF\s0 instruction with \fB\s-1IF\s0\fR and \fB\s-1UN\s0\fR to do so. .SH "NOTES on legend arguments" .IX Header "NOTES on legend arguments" .SS "Escaping the colon" .IX Subsection "Escaping the colon" A colon ':' in a \fIlegend\fR argument will mark the end of the legend. To enter a ':' as part of a legend, the colon must be escaped with a backslash '\e:'. Beware that many environments process backslashes themselves, so it may be necessary to write two backslashes in order to one being passed onto rrd_graph. .SS "String Formatting" .IX Subsection "String Formatting" The text printed below the actual graph can be formatted by appending special escape characters at the end of a text. When ever such a character occurs, all pending text is pushed onto the graph according to the character specified. .PP Valid markers are: \fB\ej\fR for justified, \fB\el\fR for left aligned, \fB\er\fR for right aligned, and \fB\ec\fR for centered. In the next section there is an example showing how to use centered formatting. .PP \&\fB\en\fR is a valid alias for \fB\el\fR since incomplete parsing in earlier versions of RRDtool lead to this behavior and a number of people has been using it. .PP Normally there are two space characters inserted between every two items printed into the graph. The space following a string can be suppressed by putting a \fB\eg\fR at the end of the string. The \fB\eg\fR also ignores any space inside the string if it is at the very end of the string. This can be used in connection with \fB\f(CB%s\fB\fR to suppress empty unit strings. .PP .Vb 1 \& GPRINT:a:MAX:%lf%s\eg .Ve .PP A special case is \s-1COMMENT:\s0\fB\es\fR which inserts some additional vertical space before placing the next row of legends. .PP If you want to have left and right aligned legends on the same line use \s-1COMMENT:\s0\fB\eu\fR to go one line back like this: .PP .Vb 3 \& COMMENT:left\el \& COMMENT:\eu \& COMMENT:right\er .Ve .PP There is also a 'nop' control for situations where you want a string to actually end in a backslash character sequence \fB\e.\fR .PP .Vb 1 \& COMMENT:OS\e2\e. .Ve .PP When using a proportional font in your graph, the tab characters or the sequence \fB\et\fR will line-up legend elements. Note that the tabs inserted are relative to the start of the current legend element! .PP Since RRDtool 1.3 is using Pango for rending text, you can use Pango markup. Pango uses the xml \fBspan\fR tags for inline formatting instructions.: .PP A simple example of a marked-up string might be: .PP .Vb 1 \& Blue text is cool! .Ve .PP The complete list of attributes for the span tag (taken from the pango documentation): .IP "\fBfont_desc\fR" 4 .IX Item "font_desc" A font description string, such as \*(L"Sans Italic 12\*(R"; note that any other span attributes will override this description. So if you have \*(L"Sans Italic\*(R" and also a style=\*(L"normal\*(R" attribute, you will get Sans normal, not italic. .IP "\fBfont_family\fR" 4 .IX Item "font_family" A font family name .IP "\fBface\fR" 4 .IX Item "face" Synonym for font_family .IP "\fBsize\fR" 4 .IX Item "size" Font size in 1024ths of a point, or one of the absolute sizes 'xx\-small', 'x\-small', 'small', 'medium', 'large', 'x\-large', 'xx\-large', or one of the relative sizes 'smaller' or 'larger'. If you want to specify a absolute size, it's usually easier to take advantage of the ability to specify a partial font description using 'font_desc'; you can use font_desc='12.5' rather than size='12800'. .IP "\fBstyle\fR" 4 .IX Item "style" One of 'normal', 'oblique', 'italic' .IP "\fBweight\fR" 4 .IX Item "weight" One of 'ultralight', 'light', 'normal', 'bold', 'ultrabold', 'heavy', or a numeric weight .IP "\fBvariant\fR" 4 .IX Item "variant" \&'normal' or 'smallcaps' .IP "\fBstretch\fR" 4 .IX Item "stretch" One of 'ultracondensed', 'extracondensed', 'condensed', 'semicondensed', 'normal', 'semiexpanded', 'expanded', 'extraexpanded', 'ultraexpanded' .IP "\fBforeground\fR" 4 .IX Item "foreground" An \s-1RGB\s0 color specification such as '#00FF00' or a color name such as 'red' .IP "\fBbackground\fR" 4 .IX Item "background" An \s-1RGB\s0 color specification such as '#00FF00' or a color name such as 'red' .IP "\fBunderline\fR" 4 .IX Item "underline" One of 'none', 'single', 'double', 'low', 'error' .IP "\fBunderline_color\fR" 4 .IX Item "underline_color" The color of underlines; an \s-1RGB\s0 color specification such as '#00FF00' or a color name such as 'red' .IP "\fBrise\fR" 4 .IX Item "rise" Vertical displacement, in 10000ths of an em. Can be negative for subscript, positive for superscript. .IP "\fBstrikethrough\fR" 4 .IX Item "strikethrough" \&'true' or 'false' whether to strike through the text .IP "\fBstrikethrough_color\fR" 4 .IX Item "strikethrough_color" The color of crossed out lines; an \s-1RGB\s0 color specification such as '#00FF00' or a color name such as 'red' .IP "\fBfallback\fR" 4 .IX Item "fallback" \&'true' or 'false' whether to enable fallback. If disabled, then characters will only be used from the closest matching font on the system. No fallback will be done to other fonts on the system that might contain the characters in the text. Fallback is enabled by default. Most applications should not disable fallback. .IP "\fBlang\fR" 4 .IX Item "lang" A language code, indicating the text language .IP "\fBletter_spacing\fR" 4 .IX Item "letter_spacing" Inter-letter spacing in 1024ths of a point. .IP "\fBgravity\fR" 4 .IX Item "gravity" One of 'south', 'east', 'north', 'west', 'auto'. .IP "\fBgravity_hint\fR" 4 .IX Item "gravity_hint" One of 'natural', 'strong', 'line'. .PP To save you some typing, there are also some shortcuts: .IP "\fBb\fR" 4 .IX Item "b" Bold .IP "\fBbig\fR" 4 .IX Item "big" Makes font relatively larger, equivalent to .IP "\fBi\fR" 4 .IX Item "i" Italic .IP "\fBs\fR" 4 .IX Item "s" Strike through .IP "\fBsub\fR" 4 .IX Item "sub" Subscript .IP "\fBsup\fR" 4 .IX Item "sup" Superscript .IP "\fBsmall\fR" 4 .IX Item "small" Makes font relatively smaller, equivalent to .IP "\fBtt\fR" 4 .IX Item "tt" Monospace font .IP "\fBu\fR" 4 .IX Item "u" Underline .SH "SEE ALSO" .IX Header "SEE ALSO" rrdgraph gives an overview of how \fBrrdtool graph\fR works. rrdgraph_data describes \fB\s-1DEF\s0\fR,\fB\s-1CDEF\s0\fR and \fB\s-1VDEF\s0\fR in detail. rrdgraph_rpn describes the \fB\s-1RPN\s0\fR language used in the \fB?DEF\fR statements. rrdgraph_graph page describes all of the graph and print functions. .PP Make sure to read rrdgraph_examples for tips&tricks. .SH "AUTHOR" .IX Header "AUTHOR" Program by Tobias Oetiker .PP This manual page by Alex van den Bogaerdt with corrections and/or additions by several people rrdtool-1.4.7/doc/rrdfetch.10000644000175300017510000003770111707501546012603 00000000000000.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.07) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "RRDFETCH 1" .TH RRDFETCH 1 "2010-11-11" "1.4.7" "rrdtool" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" rrdfetch \- Fetch data from an RRD. .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBrrdtool\fR \fBfetch\fR \fIfilename\fR \fI\s-1CF\s0\fR [\fB\-\-resolution\fR|\fB\-r\fR\ \fIresolution\fR] [\fB\-\-start\fR|\fB\-s\fR\ \fIstart\fR] [\fB\-\-end\fR|\fB\-e\fR\ \fIend\fR] [\fB\-\-daemon\fR\ \fIaddress\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" The \fBfetch\fR function is normally used internally by the graph function to get data from \fB\s-1RRD\s0\fRs. \fBfetch\fR will analyze the \fB\s-1RRD\s0\fR and try to retrieve the data in the resolution requested. The data fetched is printed to stdout. \fI*UNKNOWN*\fR data is often represented by the string \*(L"NaN\*(R" depending on your \s-1OS\s0's printf function. .IP "\fIfilename\fR" 8 .IX Item "filename" the name of the \fB\s-1RRD\s0\fR you want to fetch the data from. .IP "\fI\s-1CF\s0\fR" 8 .IX Item "CF" the consolidation function that is applied to the data you want to fetch (\s-1AVERAGE\s0,MIN,MAX,LAST) .IP "\fB\-\-resolution\fR|\fB\-r\fR \fIresolution\fR (default is the highest resolution)" 8 .IX Item "--resolution|-r resolution (default is the highest resolution)" the interval you want the values to have (seconds per value). \fBrrdfetch\fR will try to match your request, but it will return data even if no absolute match is possible. \fB\s-1NB\s0.\fR See note below. .IP "\fB\-\-start\fR|\fB\-s\fR \fIstart\fR (default end\-1day)" 8 .IX Item "--start|-s start (default end-1day)" start of the time series. 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 \s-1TIME\s0 \s-1SPECIFICATION\s0 section for a detailed explanation on ways to specify the start time. .IP "\fB\-\-end\fR|\fB\-e\fR \fIend\fR (default now)" 8 .IX Item "--end|-e end (default now)" the end of the time series in seconds since epoch. See also AT-STYLE \&\s-1TIME\s0 \s-1SPECIFICATION\s0 section for a detailed explanation of how to specify the end time. .IP "\fB\-\-daemon\fR \fIaddress\fR" 8 .IX Item "--daemon address" Address of the rrdcached daemon. If specified, a \f(CW\*(C`flush\*(C'\fR command is sent to the server before reading the \s-1RRD\s0 files. This allows \fBrrdtool\fR to return fresh data even if the daemon is configured to cache values for a long time. For a list of accepted formats, see the \fB\-l\fR option in the rrdcached manual. .Sp .Vb 1 \& rrdtool fetch \-\-daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd AVERAGE .Ve .SS "\s-1RESOLUTION\s0 \s-1INTERVAL\s0" .IX Subsection "RESOLUTION INTERVAL" In order to get RRDtool to fetch anything other than the finest resolution \s-1RRA\s0 \&\fBboth\fR the start and end time must be specified on boundaries that are multiples of the desired resolution. Consider the following example: .PP .Vb 7 \& rrdtool create subdata.rrd \-s 10 DS:ds0:GAUGE:300:0:U \e \& RRA:AVERAGE:0.5:30:3600 \e \& RRA:AVERAGE:0.5:90:1200 \e \& RRA:AVERAGE:0.5:360:1200 \e \& RRA:MAX:0.5:360:1200 \e \& RRA:AVERAGE:0.5:8640:600 \e \& RRA:MAX:0.5:8640:600 .Ve .PP This \s-1RRD\s0 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. .PP Consider now that you want to fetch the 15 minute average data for the last hour. You might try .PP .Vb 1 \& rrdtool fetch subdata.rrd AVERAGE \-r 900 \-s \-1h .Ve .PP However, this will almost always result in a time series that is \&\fB\s-1NOT\s0\fR in the 15 minute \s-1RRA\s0. Therefore, the highest resolution \s-1RRA\s0, i.e. 5 minute averages, will be chosen which in this case is not what you want. .PP Hence, make sure that .IP "1." 3 both start and end time are a multiple of 900 .IP "2." 3 both start and end time are within the desired \s-1RRA\s0 .PP So, if time now is called \*(L"t\*(R", do .PP .Vb 3 \& end time == int(t/900)*900, \& start time == end time \- 1hour, \& resolution == 900. .Ve .PP Using the bash shell, this could look be: .PP .Vb 4 \& TIME=$(date +%s) \& RRDRES=900 \& rrdtool fetch subdata.rrd AVERAGE \-r $RRDRES \e \& \-e $(($TIME/$RRDRES*$RRDRES)) \-s e\-1h .Ve .PP Or in Perl: .PP .Vb 3 \& perl \-e \*(Aq$ctime = time; $rrdres = 900; \e \& system "rrdtool fetch subdata.rrd AVERAGE \e \& \-r $rrdres \-e @{[int($ctime/$rrdres)*$rrdres]} \-s e\-1h"\*(Aq .Ve .SS "AT-STYLE \s-1TIME\s0 \s-1SPECIFICATION\s0" .IX Subsection "AT-STYLE TIME SPECIFICATION" Apart from the traditional \fISeconds since epoch\fR, RRDtool does also understand at-style time specification. The specification is called \&\*(L"at-style\*(R" after the Unix command \fIat\fR\|(1) that has moderately complex ways to specify time to run your job at a certain date and time. The at-style specification consists of two parts: the \fB\s-1TIME\s0 \s-1REFERENCE\s0\fR specification and the \fB\s-1TIME\s0 \s-1OFFSET\s0\fR specification. .SS "\s-1TIME\s0 \s-1REFERENCE\s0 \s-1SPECIFICATION\s0" .IX Subsection "TIME REFERENCE SPECIFICATION" The time reference specification is used, well, to establish a reference moment in time (to which the time offset is then applied to). When present, it should come first, when omitted, it defaults to \fBnow\fR. On its own part, time reference consists of a \fItime-of-day\fR reference (which should come first, if present) and a \fIday\fR reference. .PP The \fItime-of-day\fR can be specified as \fB\s-1HH:MM\s0\fR, \fB\s-1HH\s0.MM\fR, or just \fB\s-1HH\s0\fR. You can suffix it with \fBam\fR or \fBpm\fR or use 24\-hours clock. Some special times of day are understood as well, including \fBmidnight\fR (00:00), \fBnoon\fR (12:00) and British \&\fBteatime\fR (16:00). .PP The \fIday\fR can be specified as \fImonth-name\fR \fIday-of-the-month\fR and optional a 2\- or 4\-digit \fIyear\fR number (e.g. March 8 1999). Alternatively, you can use \fIday-of-week-name\fR (e.g. Monday), or one of the words: \&\fByesterday\fR, \fBtoday\fR, \fBtomorrow\fR. You can also specify the \fIday\fR as a full date in several numerical formats, including \fBMM/DD/[\s-1YY\s0]YY\fR, \&\fB\s-1DD\s0.MM.[\s-1YY\s0]YY\fR, or \fB\s-1YYYYMMDD\s0\fR. .PP \&\fI\s-1NOTE1\s0\fR: this is different from the original \fIat\fR\|(1) behavior, where a single-number date is interpreted as MMDD[\s-1YY\s0]YY. .PP \&\fI\s-1NOTE2\s0\fR: if you specify the \fIday\fR in this way, the \fItime-of-day\fR is \&\s-1REQUIRED\s0 as well. .PP Finally, you can use the words \fBnow\fR, \fBstart\fR, \fBend\fR or \fBepoch\fR as your time reference. \fBNow\fR refers to the current moment (and is also the default time reference). \fBStart\fR (\fBend\fR) can be used to specify a time relative to the start (end) time for those tools that use these categories (\fBrrdfetch\fR, rrdgraph) and \fBepoch\fR indicates the *IX epoch (*IX timestamp 0 = 1970\-01\-01 00:00:00 \s-1UTC\s0). \fBepoch\fR is useful to disambiguate between a timestamp value and some forms of abbreviated date/time specifications, because it allows to use time offset specifications using units, eg. \fBepoch\fR+19711205s unambiguously denotes timestamp 19711205 and not 1971\-12\-05 00:00:00 \s-1UTC\s0. .PP Month and day of the week names can be used in their naturally abbreviated form (e.g., Dec for December, Sun for Sunday, etc.). The words \fBnow\fR, \fBstart\fR, \fBend\fR can be abbreviated as \fBn\fR, \fBs\fR, \fBe\fR. .SS "\s-1TIME\s0 \s-1OFFSET\s0 \s-1SPECIFICATION\s0" .IX Subsection "TIME OFFSET SPECIFICATION" The time offset specification is used to add/subtract certain time intervals to/from the time reference moment. It consists of a \fIsign\fR (\fB+\fR\ or\ \fB\-\fR) and an \fIamount\fR. The following time units can be used to specify the \fIamount\fR: \fByears\fR, \fBmonths\fR, \fBweeks\fR, \fBdays\fR, \&\fBhours\fR, \fBminutes\fR, or \fBseconds\fR. These units can be used in singular or plural form, and abbreviated naturally or to a single letter (e.g. +3days, \-1wk, \-3y). Several time units can be combined (e.g., \-5mon1w2d) or concatenated (e.g., \-5h45min = \-5h\-45min = \&\-6h+15min = \-7h+1h30m\-15min, etc.) .PP \&\fI\s-1NOTE3\s0\fR: If you specify time offset in days, weeks, months, or years, you will end with the time offset that may vary depending on your time reference, because all those time units have no single well defined time interval value (1\ year contains either 365 or 366 days, 1\ month is 28 to 31 days long, and even 1\ day may be not equal to 24 hours twice a year, when DST-related clock adjustments take place). To cope with this, when you use days, weeks, months, or years as your time offset units your time reference date is adjusted accordingly without too much further effort to ensure anything about it (in the hope that \fImktime\fR\|(3) will take care of this later). This may lead to some surprising (or even invalid!) results, e.g. 'May\ 31\ \-1month' = 'Apr\ 31' (meaningless) = 'May\ 1' (after \fImktime\fR\|(3) normalization); in the \s-1EET\s0 timezone \&'3:30am Mar 29 1999 \-1 day' yields '3:30am Mar 28 1999' (Sunday) which is an invalid time/date combination (because of 3am \-> 4am \s-1DST\s0 forward clock adjustment, see the below example). .PP In contrast, hours, minutes, and seconds are well defined time intervals, and these are guaranteed to always produce time offsets exactly as specified (e.g. for \s-1EET\s0 timezone, '8:00\ Mar\ 27\ 1999\ +2\ days' = '8:00\ Mar\ 29\ 1999', but since there is 1\-hour \s-1DST\s0 forward clock adjustment that occurs around 3:00\ Mar\ 28\ 1999, the actual time interval between 8:00\ Mar\ 27\ 1999 and 8:00\ Mar\ 29\ 1999 equals 47 hours; on the other hand, '8:00\ Mar\ 27\ 1999\ +48\ hours' = \&'9:00\ Mar\ 29\ 1999', as expected) .PP \&\fI\s-1NOTE4\s0\fR: The single-letter abbreviation for both \fBmonths\fR and \fBminutes\fR is \fBm\fR. To disambiguate them, the parser tries to read your mind\ :) by applying the following two heuristics: .IP "1." 3 If \fBm\fR is used in context of (i.e. right after the) years, months, weeks, or days it is assumed to mean \fBmonths\fR, while in the context of hours, minutes, and seconds it means minutes. (e.g., in \-1y6m or +3w1m \fBm\fR is interpreted as \fBmonths\fR, while in \&\-3h20m or +5s2m \fBm\fR the parser decides for \fBminutes\fR). .IP "2." 3 Out of context (i.e. right after the \fB+\fR or \fB\-\fR sign) the meaning of \fBm\fR is guessed from the number it directly follows. Currently, if the number's absolute value is below 25 it is assumed that \fBm\fR means \fBmonths\fR, otherwise it is treated as \fBminutes\fR. (e.g., \-25m == \-25 minutes, while +24m == +24 months) .PP \&\fIFinal \s-1NOTES\s0\fR: Time specification is case-insensitive. Whitespace can be inserted freely or omitted altogether. There are, however, cases when whitespace is required (e.g., 'midnight\ Thu'). In this case you should either quote the whole phrase to prevent it from being taken apart by your shell or use \&'_' (underscore) or ',' (comma) which also count as whitespace (e.g., midnight_Thu or midnight,Thu). .SS "\s-1TIME\s0 \s-1SPECIFICATION\s0 \s-1EXAMPLES\s0" .IX Subsection "TIME SPECIFICATION EXAMPLES" \&\fIOct 12\fR \*(-- October 12 this year .PP \&\fI\-1month\fR or \fI\-1m\fR \*(-- current time of day, only a month before (may yield surprises, see \s-1NOTE3\s0 above). .PP \&\fInoon yesterday \-3hours\fR \*(-- yesterday morning; can also be specified as \fI9am\-1day\fR. .PP \&\fI23:59 31.12.1999\fR \*(-- 1 minute to the year 2000. .PP \&\fI12/31/99 11:59pm\fR \*(-- 1 minute to the year 2000 for imperialists. .PP \&\fI12am 01/01/01\fR \*(-- start of the new millennium .PP \&\fIend\-3weeks\fR or \fIe\-3w\fR \*(-- 3 weeks before end time (may be used as start time specification). .PP \&\fIstart+6hours\fR or \fIs+6h\fR \*(-- 6 hours after start time (may be used as end time specification). .PP \&\fI931225537\fR \*(-- 18:45 July 5th, 1999 (yes, seconds since 1970 are valid as well). .PP \&\fI19970703 12:45\fR \*(-- 12:45 July 3th, 1997 (my favorite, and its even got an \s-1ISO\s0 number (8601)). .SH "ENVIRONMENT VARIABLES" .IX Header "ENVIRONMENT VARIABLES" The following environment variables may be used to change the behavior of \&\f(CW\*(C`rrdtool\ fetch\*(C'\fR: .IP "\fB\s-1RRDCACHED_ADDRESS\s0\fR" 4 .IX Item "RRDCACHED_ADDRESS" If this environment variable is set it will have the same effect as specifying the \f(CW\*(C`\-\-daemon\*(C'\fR option on the command line. If both are present, the command line argument takes precedence. .SH "AUTHOR" .IX Header "AUTHOR" Tobias Oetiker rrdtool-1.4.7/doc/cdeftutorial.10000644000175300017510000011537111707501546013467 00000000000000.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.07) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "CDEFTUTORIAL 1" .TH CDEFTUTORIAL 1 "2010-05-10" "1.4.7" "rrdtool" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" cdeftutorial \- Alex van den Bogaerdt's CDEF tutorial .SH "DESCRIPTION" .IX Header "DESCRIPTION" Intention of this document: to provide some examples of the commonly used parts of RRDtool's \s-1CDEF\s0 language. .PP If you think some important feature is not explained properly, and if adding it to this document would benefit most users, please do ask me to add it. I will then try to provide an answer in the next release of this tutorial. No feedback equals no changes! Additions to this document are also welcome. \*(-- Alex van den Bogaerdt .SS "Why this tutorial?" .IX Subsection "Why this tutorial?" One of the powerful parts of RRDtool is its ability to do all sorts of calculations on the data retrieved from its 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. .SS "More reading" .IX Subsection "More reading" 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. .SH "What are CDEFs?" .IX Header "What are CDEFs?" When retrieving data from an \s-1RRD\s0, you are using a \*(L"\s-1DEF\s0\*(R" 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. .SH "Syntax" .IX Header "Syntax" .Vb 2 \& DEF:var_name_1=some.rrd:ds_name:CF \& CDEF:var_name_2=RPN_expression .Ve .PP You first define \*(L"var_name_1\*(R" to be data collected from data source \&\*(L"ds_name\*(R" found in \s-1RRD\s0 \*(L"some.rrd\*(R" with consolidation function \*(L"\s-1CF\s0\*(R". .PP Assume the ifInOctets \s-1SNMP\s0 counter is saved in mrtg.rrd as the \s-1DS\s0 \*(L"in\*(R". Then the following \s-1DEF\s0 defines a variable for the average of that data source: .PP .Vb 1 \& DEF:inbytes=mrtg.rrd:in:AVERAGE .Ve .PP 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 \*(L"\s-1CDEF\s0\*(R") on variable \*(L"inbytes\*(R" and use that variable (inbits) instead of the original: .PP .Vb 1 \& CDEF:inbits=inbytes,8,* .Ve .PP This tells RRDtool 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. .PP Note that the variable name used in the \s-1CDEF\s0 (inbits) must not be the same as the variable named in the \s-1DEF\s0 (inbytes)! .SH "RPN-expressions" .IX Header "RPN-expressions" \&\s-1RPN\s0 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. .PP Above multiplication by eight will look like: .IP "1." 4 Start with an empty stack .IP "2." 4 Put the content of variable inbytes on the stack .IP "3." 4 Put the number eight on the stack .IP "4." 4 Put the operation multiply on the stack .IP "5." 4 Process the stack .IP "6." 4 Retrieve the value from the stack and put it in variable inbits .PP We will now do an example with real numbers. Suppose the variable inbytes would have value 10, the stack would be: .IP "1." 4 || .IP "2." 4 |10| .IP "3." 4 |10|8| .IP "4." 4 |10|8|*| .IP "5." 4 |80| .IP "6." 4 || .PP 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: .PP .Vb 1 \& y = A \- B \-\-> y=minus(A,B) \-\-> CDEF:y=A,B,\- .Ve .PP This is not very intuitive (at least most people don't think so). For the function f(A,B) you reverse the position of \*(L"f\*(R", but you do not reverse the order of the variables. .SH "Converting your wishes to RPN" .IX Header "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 \s-1RPN\s0. .PP Suppose you have several RRDs and would like to add up some counters in them. These could be, for instance, the counters for every \s-1WAN\s0 link you are monitoring. .PP You have: .PP .Vb 3 \& router1.rrd with link1in link2in \& router2.rrd with link1in link2in \& router3.rrd with link1in link2in .Ve .PP Suppose you would like to add up all these counters, except for link2in inside router2.rrd. You need to do: .PP (in this example, \*(L"router1.rrd:link1in\*(R" means the \s-1DS\s0 link1in inside the \&\s-1RRD\s0 router1.rrd) .PP .Vb 7 \& router1.rrd:link1in \& router1.rrd:link2in \& router2.rrd:link1in \& router3.rrd:link1in \& router3.rrd:link2in \& \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- + \& (outcome of the sum) .Ve .PP As a mathematical function, this could be written: .PP \&\f(CW\*(C`add(router1.rrd:link1in , router1.rrd:link2in , router2.rrd:link1in , router3.rrd:link1in , router3.rrd:link2.in)\*(C'\fR .PP With RRDtool and \s-1RPN\s0, first, define the inputs: .PP .Vb 5 \& 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 .Ve .PP Now, the mathematical function becomes: \f(CW\*(C`add(a,b,c,d,e)\*(C'\fR .PP In \s-1RPN\s0, 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. .PP .Vb 5 \& 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 .Ve .PP What was calculated here would be written down as: .PP .Vb 1 \& ( ( ( (a+b) + c) + d) + e) > .Ve .PP This is in \s-1RPN:\s0 \f(CW\*(C`CDEF:result=a,b,+,c,+,d,+,e,+\*(C'\fR .PP 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 \s-1RPN\s0 into \f(CW\*(C`CDEF:result=a,b,c,d,e,+,+,+,+\*(C'\fR which is evaluated differently: .PP .Vb 10 \& 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) .Ve .PP As you can see the \s-1RPN\s0 expression \f(CW\*(C`a,b,c,d,e,+,+,+,+,+\*(C'\fR will evaluate in \&\f(CW\*(C`((((d+e)+c)+b)+a)\*(C'\fR and it has the same outcome as \f(CW\*(C`a,b,+,c,+,d,+,e,+\*(C'\fR. This is called the commutative law of addition, but you may forget this right away, as long as you remember what it means. .PP Now look at an expression that contains a multiplication: .PP First in normal math: \f(CW\*(C`let result = a+b*c\*(C'\fR. 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 must not alter the position of a and b. .PP You have to take this in consideration when converting this expression into \s-1RPN\s0. Read it as: \*(L"Add the outcome of b*c to a\*(R" and then it is easy to write the \s-1RPN\s0 expression: \f(CW\*(C`result=a,b,c,*,+\*(C'\fR Another expression that would return the same: \f(CW\*(C`result=b,c,*,a,+\*(C'\fR .PP In normal math, you may encounter something like \*(L"a*(b+c)\*(R" and this can also be converted into \s-1RPN\s0. 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 \s-1RPN:\s0 \f(CW\*(C`result=a,b,c,+,*\*(C'\fR. Note that this is very similar to one of the expressions in the previous paragraph, only the multiplication and the addition changed places. .PP When you have problems with \s-1RPN\s0 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. .SH "Some special numbers" .IX Header "Some special numbers" .SS "The unknown value" .IX Subsection "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(s) and calculate the missing update. You can, for instance, query your device every minute. This is creating one so called \s-1PDP\s0 or primary data point per minute. If you defined your \s-1RRD\s0 to contain an \s-1RRA\s0 that stores 5\-minute values, you need five of those PDPs to create one \s-1CDP\s0 (consolidated data point). These PDPs can become unknown in two cases: .IP "1." 4 The updates are too far apart. This is tuned using the \*(L"heartbeat\*(R" setting. .IP "2." 4 The update was set to unknown on purpose by inserting no value (using the template option) or by using \*(L"U\*(R" as the value to insert. .PP When a \s-1CDP\s0 is calculated, another mechanism determines if this \s-1CDP\s0 is valid or not. If there are too many PDPs unknown, the \s-1CDP\s0 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 \&\s-1PDP\s0 per \s-1CDP\s0, this makes the \s-1CDP\s0 go unknown! .PP Suppose the counter increments with one per second and you retrieve it every minute: .PP .Vb 7 \& counter value resulting rate \& 10\*(Aq000 \& 10\*(Aq060 1; (10\*(Aq060\-10\*(Aq000)/60 == 1 \& 10\*(Aq120 1; (10\*(Aq120\-10\*(Aq060)/60 == 1 \& unknown unknown; you don\*(Aqt know the last value \& 10\*(Aq240 unknown; you don\*(Aqt know the previous value \& 10\*(Aq300 1; (10\*(Aq300\-10\*(Aq240)/60 == 1 .Ve .PP If the \s-1CDP\s0 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 \s-1CDP\s0 would have a known value of 1. If xff would have been set to 0.2 then the resulting \s-1CDP\s0 would be unknown. .PP You have to decide the proper values for heartbeat, number of PDPs per \&\s-1CDP\s0 and the xff factor. As you can see from the previous text they define the behavior of your \s-1RRA\s0. .SS "Working with unknown data in your database" .IX Subsection "Working with unknown data in your database" As you have read in the previous chapter, entries in an \s-1RRA\s0 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 \f(CW\*(C`result=a,b,+\*(C'\fR 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 \*(L"unknown\*(R" means \*(L"zero\*(R" and this is not true. .PP There has been a case where somebody was collecting data for over a year. A new piece of equipment was installed, a new \s-1RRD\s0 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. .PP In this case, it is fairly reasonable to use a \s-1CDEF\s0 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). .PP There are some examples below that make this change. .SS "Infinity" .IX Subsection "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 can think of positive and negative infinity depending on the position relative to zero. .PP 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). .PP Infinity in RRDtool is mostly used to draw an \s-1AREA\s0 without knowing its vertical dimensions. You can think of it as drawing an \s-1AREA\s0 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. .SS "Working with unknown data and infinity" .IX Subsection "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. .SH "Some examples" .IX Header "Some examples" .SS "Example: using a recently created \s-1RRD\s0" .IX Subsection "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. .PP 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: .IP "\(bu" 4 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. .IP "\(bu" 4 Alternatively, you could use \s-1CDEF\s0 and alter unknown data to zero. .PP 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 manually. Implementing the second method is described next: .PP What we want is: \*(L"if the value is unknown, replace it with zero\*(R". This could be written in pseudo-code as: if (value is unknown) then (zero) else (value). When reading the rrdgraph manual you notice the \*(L"\s-1UN\s0\*(R" function that returns zero or one. You also notice the \*(L"\s-1IF\s0\*(R" function that takes zero or one as input. .PP First look at the \*(L"\s-1IF\s0\*(R" 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 \*(L"true\*(R" and if not, the third value is returned to the stack. We want the \*(L"\s-1UN\s0\*(R" function to decide what happens so we combine those two functions in one \s-1CDEF\s0. .PP Lets write down the two possible paths for the \*(L"\s-1IF\s0\*(R" function: .PP .Vb 2 \& if true return a \& if false return b .Ve .PP In \s-1RPN:\s0 \f(CW\*(C`result=x,a,b,IF\*(C'\fR where \*(L"x\*(R" is either true or false. .PP Now we have to fill in \*(L"x\*(R", this should be the \*(L"(value is unknown)\*(R" part and this is in \s-1RPN:\s0 \f(CW\*(C`result=value,UN\*(C'\fR .PP We now combine them: \f(CW\*(C`result=value,UN,a,b,IF\*(C'\fR and when we fill in the appropriate things for \*(L"a\*(R" and \*(L"b\*(R" we're finished: .PP \&\f(CW\*(C`CDEF:result=value,UN,0,value,IF\*(C'\fR .PP You may want to read Steve Rader's \s-1RPN\s0 guide if you have difficulties with the way I explained this last example. .PP If you want to check this \s-1RPN\s0 expression, just mimic RRDtool behavior: .PP .Vb 4 \& 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 .Ve .PP Of course, if you would like to see another value instead of zero, you can use that other value. .PP Eventually, when all unknown data is removed from the \s-1RRD\s0, you may want to remove this rule so that unknown data is properly displayed. .SS "Example: better handling of unknown data, by using time" .IX Subsection "Example: better handling of unknown data, by using time" The 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: .IP "\(bu" 4 If there is unknown data, look at the time that this sample was taken. .IP "\(bu" 4 If the unknown value is before time xxx, make it zero. .IP "\(bu" 4 If it is after time xxx, leave it as unknown data. .PP 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, 1999, 00:35:57 \s-1MET\s0 \s-1DST\s0. Translate this time in seconds since 1970\-01\-01 and it becomes 937'521'357. If you process unknown values that were received after this time, you want to leave them unknown and if they were \*(L"received\*(R" before this time, you want to translate them into zero (so you can effectively ignore them while adding them to your other routers counters). .PP Translating Friday September 17, 1999, 00:35:57 \s-1MET\s0 \s-1DST\s0 into 937'521'357 can be done by, for instance, using gnu date: .PP .Vb 1 \& date \-d "19990917 00:35:57" +%s .Ve .PP 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. .PP 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: .IP "1." 4 If the timestamp of the value is after 937'521'357, leave it as is. .IP "2." 4 If the value is a known value, leave it as is. .IP "3." 4 Change the unknown value into zero. .PP Lets look at part one: .PP .Vb 1 \& if (true) return the original value .Ve .PP We rewrite this: .PP .Vb 2 \& if (true) return "a" \& if (false) return "b" .Ve .PP 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, \*(L"\s-1TIME\s0\*(R". This time has to be compared to a constant number, we need \*(L"\s-1GT\s0\*(R". The output of \*(L"\s-1GT\s0\*(R" is true or false and this is good input to \*(L"\s-1IF\s0\*(R". We want \*(L"if (time > 937521357) then (return a) else (return b)\*(R". .PP This process was already described thoroughly in the previous chapter so lets do it quick: .PP .Vb 4 \& 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 .Ve .PP We end up with: \&\f(CW\*(C`CDEF:result=TIME,937521357,GT,value,value,UN,0,value,IF,IF\*(C'\fR .PP This looks very complex, however, as you can see, it was not too hard to come up with. .SS "Example: Pretending weird data isn't there" .IX Subsection "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. .PP There are two options: .IP "1." 4 If the number exceeds 100kb/s it is wrong and you want it masked out by changing it into unknown. .IP "2." 4 You don't want the graph to show more than 100kb/s. .PP Pseudo code: if (number > 100) then unknown else number or Pseudo code: if (number > 100) then 100 else number. .PP The second \*(L"problem\*(R" 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. .PP We use \*(L"\s-1IF\s0\*(R" and \*(L"\s-1GT\s0\*(R" again. \*(L"if (x) then (y) else (z)\*(R" is written down as \*(L"CDEF:result=x,y,z,IF\*(R"; now fill in x, y and z. For x you fill in \*(L"number greater than 100kb/s\*(R" becoming \&\*(L"number,100000,GT\*(R" (kilo is 1'000 and b/s is what we measure!). The \*(L"z\*(R" part is \*(L"number\*(R" in both cases and the \*(L"y\*(R" part is either \&\*(L"\s-1UNKN\s0\*(R" for unknown or \*(L"100000\*(R" for 100kb/s. .PP The two \s-1CDEF\s0 expressions would be: .PP .Vb 2 \& CDEF:result=number,100000,GT,UNKN,number,IF \& CDEF:result=number,100000,GT,100000,number,IF .Ve .SS "Example: working on a certain time span" .IX Subsection "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 \*(L"hide\*(R" the rest of the time frame. Don't ask me when this would be useful, it's just here for the example :) .PP We need to compare the time stamp to a begin date and an end date. Comparing isn't difficult: .PP .Vb 2 \& TIME,begintime,GE \& TIME,endtime,LE .Ve .PP These two parts of the \s-1CDEF\s0 produce either 0 for false or 1 for true. We can now check if they are both 0 (or 1) using a few \s-1IF\s0 statements but, as Wataru Satoh pointed out, we can use the \*(L"*\*(R" or \*(L"+\*(R" functions as logical \s-1AND\s0 and logical \s-1OR\s0. .PP For \*(L"*\*(R", the result will be zero (false) if either one of the two operators is zero. For \*(L"+\*(R", 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 \*(L"true\*(R". This means that, for instance, \&\*(L"\-1,1,+\*(R" (which should be \*(L"true or true\*(R") will become \s-1FALSE\s0 ... In other words, use \*(L"+\*(R" only if you know for sure that you have positive numbers (or zero) only. .PP Let's compile the complete \s-1CDEF:\s0 .PP .Vb 2 \& DEF:ds0=router1.rrd:AVERAGE \& CDEF:ds0modified=TIME,begintime,GT,TIME,endtime,LE,*,ds0,UNKN,IF .Ve .PP This will return the value of ds0 if both comparisons return true. You could also do it the other way around: .PP .Vb 2 \& DEF:ds0=router1.rrd:AVERAGE \& CDEF:ds0modified=TIME,begintime,LT,TIME,endtime,GT,+,UNKN,ds0,IF .Ve .PP This will return an \s-1UNKNOWN\s0 if either comparison returns true. .SS "Example: You suspect to have problems and want to see unknown data." .IX Subsection "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 \*(L"NaN\*(R" in the database (\*(L"Not a Number\*(R") and NaN is evaluated as Unknown. .PP In this case, you would like to be alerted to it and the sum of the remaining values is of no value to you. .PP It would be something like: .PP .Vb 5 \& 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,+,+,+ .Ve .PP 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. .PP Define an extra \s-1CDEF\s0 that is unknown if all is okay and is infinite if there is an unknown value: .PP .Vb 1 \& CDEF:wrongdata=allusers,UN,INF,UNKN,IF .Ve .PP \&\*(L"allusers,UN\*(R" will evaluate to either true or false, it is the (x) part of the \*(L"\s-1IF\s0\*(R" function and it checks if allusers is unknown. The (y) part of the \*(L"\s-1IF\s0\*(R" function is set to \*(L"\s-1INF\s0\*(R" (which means infinity) and the (z) part of the function returns \*(L"\s-1UNKN\s0\*(R". .PP The logic is: if (allusers == unknown) then return \s-1INF\s0 else return \s-1UNKN\s0. .PP You can now use \s-1AREA\s0 to display this \*(L"wrongdata\*(R" in bright red. If it is unknown (because allusers is known) then the red \s-1AREA\s0 won't show up. If the value is \s-1INF\s0 (because allusers is unknown) then the red \s-1AREA\s0 will be filled in on the graph at that particular time. .PP .Vb 2 \& AREA:allusers#0000FF:combined user count \& AREA:wrongdata#FF0000:unknown data .Ve .SS "Same example useful with STACKed data:" .IX Subsection "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 \*(L"normal\*(R" graph: .PP .Vb 11 \& 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 .Ve .PP If there is unknown data in one of users1..users4, the \*(L"wrongdata\*(R" \s-1AREA\s0 will be drawn and because it starts at the X\-axis and has infinite height it will effectively overwrite the STACKed parts. .PP You could combine the two \s-1CDEF\s0 lines into one (we don't use \*(L"allusers\*(R") if you like. But there are good reasons for writing two \s-1CDEFS:\s0 .IP "\(bu" 4 It improves the readability of the script. .IP "\(bu" 4 It can be used inside \s-1GPRINT\s0 to display the total number of users. .PP If you choose to combine them, you can substitute the \*(L"allusers\*(R" in the second \s-1CDEF\s0 with the part after the equal sign from the first line: .PP .Vb 1 \& CDEF:wrongdata=users1,users2,users3,users4,+,+,+,UN,INF,UNKN,IF .Ve .PP If you do so, you won't be able to use these next GPRINTs: .PP .Vb 5 \& 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\en" .Ve .SH "The examples from the RRD graph manual page" .IX Header "The examples from the RRD graph manual page" .SS "Degrees Celsius vs. Degrees Fahrenheit" .IX Subsection "Degrees Celsius vs. Degrees Fahrenheit" To convert Celsius into Fahrenheit use the formula F=9/5*C+32 .PP .Vb 5 \& rrdtool graph demo.png \-\-title="Demo Graph" \e \& DEF:cel=demo.rrd:exhaust:AVERAGE \e \& CDEF:far=9,5,/,cel,*,32,+ \e \& LINE2:cel#00a000:"D. Celsius" \e \& LINE2:far#ff0000:"D. Fahrenheit\ec" .Ve .PP This example gets the \s-1DS\s0 called \*(L"exhaust\*(R" from database \*(L"demo.rrd\*(R" and puts the values in variable \*(L"cel\*(R". The \s-1CDEF\s0 used is evaluated as follows: .PP .Vb 10 \& CDEF:far=9,5,/,cel,*,32,+ \& 1. push 9, push 5 \& 2. push function "divide" and process it \& the stack now contains 9/5 \& 3. push variable "cel" \& 4. push function "multiply" and process it \& the stack now contains 9/5*cel \& 5. push 32 \& 6. push function "plus" and process it \& the stack contains now the temperature in Fahrenheit .Ve .SS "Changing unknown into zero" .IX Subsection "Changing unknown into zero" .Vb 9 \& rrdtool graph demo.png \-\-title="Demo Graph" \e \& DEF:idat1=interface1.rrd:ds0:AVERAGE \e \& DEF:idat2=interface2.rrd:ds0:AVERAGE \e \& DEF:odat1=interface1.rrd:ds1:AVERAGE \e \& DEF:odat2=interface2.rrd:ds1:AVERAGE \e \& CDEF:agginput=idat1,UN,0,idat1,IF,idat2,UN,0,idat2,IF,+,8,* \e \& CDEF:aggoutput=odat1,UN,0,odat1,IF,odat2,UN,0,odat2,IF,+,8,* \e \& AREA:agginput#00cc00:Input Aggregate \e \& LINE1:aggoutput#0000FF:Output Aggregate .Ve .PP These two CDEFs are built from several functions. It helps to split them when viewing what they do. Starting with the first \s-1CDEF\s0 we would get: .PP .Vb 4 \& idat1,UN \-\-> a \& 0 \-\-> b \& idat1 \-\-> c \& if (a) then (b) else (c) .Ve .PP The result is therefore \*(L"0\*(R" if it is true that \*(L"idat1\*(R" equals \*(L"\s-1UN\s0\*(R". If not, the original value of \*(L"idat1\*(R" is put back on the stack. Lets call this answer \*(L"d\*(R". The process is repeated for the next five items on the stack, it is done the same and will return answer \&\*(L"h\*(R". The resulting stack is therefore \*(L"d,h\*(R". The expression has been simplified to \*(L"d,h,+,8,*\*(R" and it will now be easy to see that we add \*(L"d\*(R" and \*(L"h\*(R", and multiply the result with eight. .PP The end result is that we have added \*(L"idat1\*(R" and \*(L"idat2\*(R" and in the process we effectively ignored unknown values. The result is multiplied by eight, most likely to convert bytes/s to bits/s. .SS "Infinity demo" .IX Subsection "Infinity demo" .Vb 10 \& rrdtool graph example.png \-\-title="INF demo" \e \& DEF:val1=some.rrd:ds0:AVERAGE \e \& DEF:val2=some.rrd:ds1:AVERAGE \e \& DEF:val3=some.rrd:ds2:AVERAGE \e \& DEF:val4=other.rrd:ds0:AVERAGE \e \& CDEF:background=val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF \e \& CDEF:wipeout=val1,val2,val3,val4,+,+,+,UN,INF,UNKN,IF \e \& AREA:background#F0F0F0 \e \& AREA:val1#0000FF:Value1 \e \& STACK:val2#00C000:Value2 \e \& STACK:val3#FFFF00:Value3 \e \& STACK:val4#FFC000:Value4 \e \& AREA:whipeout#FF0000:Unknown .Ve .PP This demo demonstrates two ways to use infinity. It is a bit tricky to see what happens in the \*(L"background\*(R" \s-1CDEF\s0. .PP .Vb 1 \& "val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF" .Ve .PP This \s-1RPN\s0 takes the value of \*(L"val4\*(R" as input and then immediately removes it from the stack using \*(L"\s-1POP\s0\*(R". The stack is now empty but as a side effect we now know the time that this sample was taken. This time is put on the stack by the \*(L"\s-1TIME\s0\*(R" function. .PP \&\*(L"\s-1TIME\s0,7200,%\*(R" takes the modulo of time and 7'200 (which is two hours). The resulting value on the stack will be a number in the range from 0 to 7199. .PP 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, \*(L"16,3,%\*(R" returns 1. .PP We have the result of \*(L"\s-1TIME\s0,7200,%\*(R" on the stack, lets call this \&\*(L"a\*(R". The start of the \s-1RPN\s0 has become \*(L"a,3600,LE\*(R" and this checks if \*(L"a\*(R" is less or equal than \*(L"3600\*(R". It is true half of the time. We now have to process the rest of the \s-1RPN\s0 and this is only a simple \&\*(L"\s-1IF\s0\*(R" function that returns either \*(L"\s-1INF\s0\*(R" or \*(L"\s-1UNKN\s0\*(R" depending on the time. This is returned to variable \*(L"background\*(R". .PP The second \s-1CDEF\s0 has been discussed earlier in this document so we won't do that here. .PP 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). .PP 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 \s-1CDEF\s0, it will overlay the data with an \s-1AREA\s0 so the data cannot be seen anymore. .PP 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 \*(L"wipeout\*(R" variable and place a negative sign before it: \*(L"CDEF:wipeout2=wipeout,\-1,*\*(R" .SS "Filtering data" .IX Subsection "Filtering data" You may do some complex data filtering: .PP .Vb 1 \& 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:\*(Aqraw data\*(Aq \& \& \& DERIVATE: \& \& DEF:var=database.rrd:traffic:AVERAGE \& CDEF:prev1=PREV(var) \& CDEF:time=var,POP,TIME \& CDEF:prevtime=PREV(time) \& CDEF:derivate=var,prev1,\-,time,prevtime,\-,/ \& LINE3:derivate#000077:derivate \& LINE1:var#007700:\*(Aqraw data\*(Aq .Ve .SH "Out of ideas for now" .IX Header "Out of ideas for now" This document was created from questions asked by either myself or by other people on the RRDtool mailing 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: .PP Remember: \fBNo feedback equals no changes!\fR .SH "SEE ALSO" .IX Header "SEE ALSO" The RRDtool manpages .SH "AUTHOR" .IX Header "AUTHOR" Alex van den Bogaerdt rrdtool-1.4.7/doc/rrdupdate.10000644000175300017510000002541011707501546012766 00000000000000.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.07) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "RRDUPDATE 1" .TH RRDUPDATE 1 "2009-06-02" "1.4.7" "rrdtool" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" rrdupdate \- Store a new set of values into the RRD .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBrrdtool\fR {\fBupdate\fR | \fBupdatev\fR} \fIfilename\fR [\fB\-\-template\fR|\fB\-t\fR\ \fIds-name\fR[\fB:\fR\fIds-name\fR]...] [\fB\-\-daemon\fR\ \fIaddress\fR] [\fB\-\-\fR] \&\fBN\fR|\fItimestamp\fR\fB:\fR\fIvalue\fR[\fB:\fR\fIvalue\fR...] \&\fIat-timestamp\fR\fB@\fR\fIvalue\fR[\fB:\fR\fIvalue\fR...] [\fItimestamp\fR\fB:\fR\fIvalue\fR[\fB:\fR\fIvalue\fR...]\ ...] .SH "DESCRIPTION" .IX Header "DESCRIPTION" The \fBupdate\fR function feeds new data values into an \fB\s-1RRD\s0\fR. The data is time aligned (interpolated) according to the properties of the \&\fB\s-1RRD\s0\fR to which the data is written. .IP "\fBupdatev\fR" 8 .IX Item "updatev" This alternate version of \fBupdate\fR takes the same arguments and performs the same function. The \fIv\fR stands for \fIverbose\fR, which describes the output returned. \fBupdatev\fR returns a list of any and all consolidated data points (CDPs) written to disk as a result of the invocation of update. The values are indexed by timestamp (time_t), \&\s-1RRA\s0 (consolidation function and PDPs per \s-1CDP\s0), and data source (name). Note that depending on the arguments of the current and previous call to update, the list may have no entries or a large number of entries. .Sp Since \fBupdatev\fR requires direct disk access, the \fB\-\-daemon\fR option cannot be used with this command. .IP "\fIfilename\fR" 8 .IX Item "filename" The name of the \fB\s-1RRD\s0\fR you want to update. .IP "\fB\-\-template\fR|\fB\-t\fR \fIds-name\fR[\fB:\fR\fIds-name\fR]..." 8 .IX Item "--template|-t ds-name[:ds-name]..." By default, the \fBupdate\fR function expects its data input in the order the data sources are defined in the \s-1RRD\s0, excluding any \s-1COMPUTE\s0 data sources (i.e. if the third data source \fB\s-1DST\s0\fR is \s-1COMPUTE\s0, the third input value will be mapped to the fourth data source in the \fB\s-1RRD\s0\fR and so on). This is not very error resistant, as you might be sending the wrong data into an \s-1RRD\s0. .Sp The template switch allows you to specify which data sources you are going to update and in which order. If the data sources specified in the template are not available in the \s-1RRD\s0 file, the update process will abort with an error message. .Sp While it appears possible with the template switch to update data sources asynchronously, \fBRRDtool\fR implicitly assigns non-COMPUTE data sources missing from the template the \fI*UNKNOWN*\fR value. .Sp Do not specify a value for a \s-1COMPUTE\s0 \fB\s-1DST\s0\fR in the \fBupdate\fR function. If this is done accidentally (and this can only be done using the template switch), \fBRRDtool\fR will ignore the value specified for the \s-1COMPUTE\s0 \fB\s-1DST\s0\fR. .IP "\fB\-\-daemon\fR \fIaddress\fR" 8 .IX Item "--daemon address" If given, \fBRRDTool\fR will try to connect to the caching daemon rrdcached at \fIaddress\fR and will fail if the connection cannot be established. If the connection is successfully established the values will be sent to the daemon instead of accessing the files directly. .Sp For a list of accepted formats, see the \fB\-l\fR option in the rrdcached manual. .IP "\fBN\fR|\fItimestamp\fR\fB:\fR\fIvalue\fR[\fB:\fR\fIvalue\fR...]" 8 .IX Item "N|timestamp:value[:value...]" The data used for updating the \s-1RRD\s0 was acquired at a certain time. This time can either be defined in seconds since 1970\-01\-01 or by using the letter 'N', in which case the update time is set to be the current time. Negative time values are subtracted from the current time. An \s-1AT_STYLE\s0 \s-1TIME\s0 \s-1SPECIFICATION\s0 (see the \fIrrdfetch\fR documentation) may also be used by delimiting the end of the time specification with the '@' character instead of a ':'. Getting the timing right to the second is especially important when you are working with data-sources of type \fB\s-1COUNTER\s0\fR, \fB\s-1DERIVE\s0\fR or \&\fB\s-1ABSOLUTE\s0\fR. .Sp When using negative time values, options and data have to be separated by two dashes (\fB\-\-\fR), else the time value would be parsed as an option. See below for an example. .Sp When using negative time values, options and data have to be separated by two dashes (\fB\-\-\fR), else the time value would be parsed as an option. See below for an example. .Sp The remaining elements of the argument are \s-1DS\s0 updates. The order of this list is the same as the order the data sources were defined in the \s-1RRA\s0. If there is no data for a certain data-source, the letter \&\fBU\fR (e.g., N:0.1:U:1) can be specified. .Sp The format of the value acquired from the data source is dependent on the data source type chosen. Normally it will be numeric, but the data acquisition modules may impose their very own parsing of this parameter as long as the colon (\fB:\fR) remains the data source value separator. .SH "ENVIRONMENT VARIABLES" .IX Header "ENVIRONMENT VARIABLES" The following environment variables may be used to change the behavior of \&\f(CW\*(C`rrdtool\ update\*(C'\fR: .IP "\fB\s-1RRDCACHED_ADDRESS\s0\fR" 4 .IX Item "RRDCACHED_ADDRESS" If this environment variable is set it will have the same effect as specifying the \f(CW\*(C`\-\-daemon\*(C'\fR option on the command line. If both are present, the command line argument takes precedence. .SH "EXAMPLES" .IX Header "EXAMPLES" .IP "\(bu" 4 \&\f(CW\*(C`rrdtool update demo1.rrd N:3.44:3.15:U:23\*(C'\fR .Sp Update the database file demo1.rrd with 3 known and one \fI*UNKNOWN*\fR value. Use the current time as the update time. .IP "\(bu" 4 \&\f(CW\*(C`rrdtool update demo2.rrd 887457267:U 887457521:22 887457903:2.7\*(C'\fR .Sp Update the database file demo2.rrd which expects data from a single data-source, three times. First with an \fI*UNKNOWN*\fR value then with two regular readings. The update interval seems to be around 300 seconds. .IP "\(bu" 4 \&\f(CW\*(C`rrdtool update demo3.rrd \-\- \-5:21 N:42\*(C'\fR .Sp Update the database file demo3.rrd two times, using five seconds in the past and the current time as the update times. .IP "\(bu" 4 \&\f(CW\*(C`rrdtool update \-\-cache /var/lib/rrd/demo3.rrd N:42\*(C'\fR .Sp Update the file \f(CW\*(C`/var/lib/rrd/demo3.rrd\*(C'\fR with a single data source, using the current time. If the caching daemon cannot be reached, do \fBnot\fR fall back to direct file access. .IP "\(bu" 4 \&\f(CW\*(C`rrdtool update \-\-daemon unix:/tmp/rrdd.sock demo4.rrd N:23\*(C'\fR .Sp Use the \s-1UNIX\s0 domain socket \f(CW\*(C`/tmp/rrdd.sock\*(C'\fR to contact the caching daemon. If the caching daemon is not available, update the file \f(CW\*(C`demo4.rrd\*(C'\fR directly. \&\fB\s-1WARNING:\s0\fR Since a relative path is specified, the following disturbing effect may occur: If the daemon is available, the file relative to the working directory \fBof the daemon\fR is used. If the daemon is not available, the file relative to the current working directory of the invoking process is used. \&\fBThis may update two different files depending on whether the daemon could be reached or not.\fR Don't do relative paths, kids! .SH "AUTHORS" .IX Header "AUTHORS" Tobias Oetiker , Florian Forster rrdtool-1.4.7/doc/rrdrestore.10000644000175300017510000001121011707501546013160 00000000000000.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.07) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "RRDRESTORE 1" .TH RRDRESTORE 1 "2008-03-15" "1.4.7" "rrdtool" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" rrdrestore \- Restore the contents of an RRD from its XML dump format .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBrrdtool\fR \fBrestore\fR \fIfilename.xml\fR \fIfilename.rrd\fR [\fB\-\-range\-check\fR|\fB\-r\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" The \fBrestore\fR function reads the \s-1XML\s0 representation of an \s-1RRD\s0 and converts it to the native \fB\s-1RRD\s0\fR format. .IP "\fIfilename.xml\fR" 8 .IX Item "filename.xml" The name of the \fB\s-1XML\s0\fR file you want to restore. .IP "\fIfilename.rrd\fR" 8 .IX Item "filename.rrd" The name of the \fB\s-1RRD\s0\fR to restore. .IP "\fB\-\-range\-check\fR|\fB\-r\fR" 8 .IX Item "--range-check|-r" Make sure the values in the RRAs do not exceed the limits defined for the various data sources. .IP "\fB\-\-force\-overwrite\fR|\fB\-f\fR" 8 .IX Item "--force-overwrite|-f" Allows \fBRRDtool\fR to overwrite the destination \fB\s-1RRD\s0\fR. .SH "AUTHOR" .IX Header "AUTHOR" Tobias Oetiker rrdtool-1.4.7/doc/rrdgraph_examples.10000644000175300017510000002663511707501546014515 00000000000000.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.07) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "RRDGRAPH_EXAMPLES 1" .TH RRDGRAPH_EXAMPLES 1 "2009-02-21" "1.4.7" "rrdtool" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" rrdgraph_examples \- Examples for rrdtool graph .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBrrdtool graph /home/httpd/html/test.png \-\-img\-format \s-1PNG\s0\fR .PP followed by any of the examples below .SH "DESCRIPTION" .IX Header "DESCRIPTION" For your convenience some of the commands are explained here by using detailed examples. They are not always cut-and-paste ready because comments are intermixed with the examples. .SH "EXAMPLES" .IX Header "EXAMPLES" .SS "Data with multiple resolutions" .IX Subsection "Data with multiple resolutions" .Vb 7 \& \-\-end now \-\-start end\-120000s \-\-width 400 \& DEF:ds0a=/home/rrdtool/data/router1.rrd:ds0:AVERAGE \& DEF:ds0b=/home/rrdtool/data/router1.rrd:ds0:AVERAGE:step=1800 \& DEF:ds0c=/home/rrdtool/data/router1.rrd:ds0:AVERAGE:step=7200 \& LINE1:ds0a#0000FF:"default resolution\el" \& LINE1:ds0b#00CCFF:"resolution 1800 seconds per interval\el" \& LINE1:ds0c#FF00FF:"resolution 7200 seconds per interval\el" .Ve .SS "Nicely formatted legend section" .IX Subsection "Nicely formatted legend section" .Vb 10 \& DEF:ds0=/home/rrdtool/data/router1.rrd:ds0:AVERAGE \& DEF:ds1=/home/rrdtool/data/router1.rrd:ds1:AVERAGE \& VDEF:ds0max=ds0,MAXIMUM \& VDEF:ds0avg=ds0,AVERAGE \& VDEF:ds0min=ds0,MINIMUM \& VDEF:ds0pct=ds0,95,PERCENT \& VDEF:ds1max=ds1,MAXIMUM \& VDEF:ds1avg=ds1,AVERAGE \& VDEF:ds1min=ds1,MINIMUM \& VDEF:ds1pct=ds1,95,PERCENT .Ve .PP Note: consolidation occurs here. .PP .Vb 2 \& CDEF:ds0bits=ds0,8,* \& CDEF:ds1bits=ds1,8,* .Ve .PP Note: 10 spaces to move text to the right .PP .Vb 1 \& COMMENT:" " .Ve .PP Note: the column titles have to be as wide as the columns .PP .Vb 3 \& COMMENT:"Maximum " \& COMMENT:"Average " \& COMMENT:"Minimum " \& \& COMMENT:"95th percentile\el" \& AREA:ds0bits#00C000:"Inbound " \& GPRINT:ds0max:"%6.2lf %Sbps" \& GPRINT:ds0avg:"%6.2lf %Sbps" \& GPRINT:ds0min:"%6.2lf %Sbps" \& GPRINT:ds0pct:"%6.2lf %Sbps\el" \& LINE1:ds1bits#0000FF:"Outbound" \& GPRINT:ds1max:"%6.2lf %Sbps" \& GPRINT:ds1avg:"%6.2lf %Sbps" \& GPRINT:ds1min:"%6.2lf %Sbps" \& GPRINT:ds1pct:"%6.2lf %Sbps\el" .Ve .SS "Offsetting a line on the y\-axis" .IX Subsection "Offsetting a line on the y-axis" Depending on your needs you can do this in two ways: .IP "\(bu" 4 Offset the data, then graph this .Sp .Vb 1 \& DEF:mydata=my.rrd:ds:AVERAGE .Ve .Sp Note: this will also influence any other command that uses \*(L"data\*(R" .Sp .Vb 2 \& CDEF:data=mydata,100,+ \& LINE1:data#FF0000:"Data with offset" .Ve .IP "\(bu" 4 Graph the original data, with an offset .Sp .Vb 1 \& DEF:mydata=my.rrd:ds:AVERAGE .Ve .Sp Note: no color in the first line so it is not visible .Sp .Vb 1 \& LINE1:100 .Ve .Sp Note: the second line gets stacked on top of the first one .Sp .Vb 1 \& LINE1:data#FF0000:"Data with offset":STACK .Ve .SS "Drawing dashed lines" .IX Subsection "Drawing dashed lines" Also works for \s-1HRULE\s0 and \s-1VRULE\s0 .IP "\(bu" 4 default style: \- \- \- \- \- LINE1:data#FF0000:\*(L"dashed line\*(R":dashes .IP "\(bu" 4 more fancy style with offset: \- \- \-\-\- \- \-\-\- \- LINE1:data#FF0000:\*(L"another dashed line\*(R":dashes=15,5,5,10:dash\-offset=10 .SS "Time ranges" .IX Subsection "Time ranges" .Vb 6 \& Last four weeks: \-\-start end\-4w \-\-end 00:00 \& January 2001: \-\-start 20010101 \-\-end start+31d \& January 2001: \-\-start 20010101 \-\-end 20010201 \& Last hour: \-\-start end\-1h \& Last 24 hours: \& Yesterday: \-\-end 00:00 .Ve .SS "Viewing the current and previous week together" .IX Subsection "Viewing the current and previous week together" .Vb 3 \& \-\-end now \-\-start end\-1w \& DEF:thisweek=router.rrd:ds0:AVERAGE \& DEF:lastweek=router.rrd:ds0:AVERAGE:end=now\-1w:start=end\-1w .Ve .PP Shift the data forward by one week (604800 seconds) .PP .Vb 4 \& SHIFT:lastweek:604800 \& [ more of the usual VDEF and CDEF stuff if you like ] \& AREA:lastweek#0000FF:Last\e week \& LINE1:thisweek#FF0000:This\e week .Ve .SS "Aberrant Behaviour Detection" .IX Subsection "Aberrant Behaviour Detection" If the specialized function \fBRRAs\fR exist for aberrant behavior detection, they can be used to generate the graph of a time series with confidence bands and failures. .PP .Vb 10 \& rrdtool graph example.png \e \& DEF:obs=monitor.rrd:ifOutOctets:AVERAGE \e \& DEF:pred=monitor.rrd:ifOutOctets:HWPREDICT \e \& DEF:dev=monitor.rrd:ifOutOctets:DEVPREDICT \e \& DEF:fail=monitor.rrd:ifOutOctets:FAILURES \e \& TICK:fail#ffffa0:1.0:"Failures\e: Average bits out" \e \& CDEF:scaledobs=obs,8,* \e \& CDEF:upper=pred,dev,2,*,+ \e \& CDEF:lower=pred,dev,2,*,\- \e \& CDEF:scaledupper=upper,8,* \e \& CDEF:scaledlower=lower,8,* \e \& LINE2:scaledobs#0000ff:"Average bits out" \e \& LINE1:scaledupper#ff0000:"Upper Confidence Bound: Average bits out" \e \& LINE1:scaledlower#ff0000:"Lower Confidence Bound: Average bits out" .Ve .PP This example generates a graph of the data series in blue (\s-1LINE2\s0 with the scaledobs virtual data source), confidence bounds in red (scaledupper and scaledlower virtual data sources), and potential failures (i.e. potential aberrant aberrant behavior) marked by vertical yellow lines (the fail data source). .PP The raw data comes from an \s-1AVERAGE\s0 \fB\s-1RRA\s0\fR, the finest resolution of the observed time series (one consolidated data point per primary data point). The predicted (or smoothed) values are stored in the \s-1HWPREDICT\s0 \fB\s-1RRA\s0\fR. The predicted deviations (think standard deviation) values are stored in the \s-1DEVPREDICT\s0 \fB\s-1RRA\s0\fR. Finally, the \s-1FAILURES\s0 \fB\s-1RRA\s0\fR contains indicators, with 1 denoting a potential failure. .PP All of the data is rescaled to bits (instead of Octets) by multiplying by 8. The confidence bounds are computed by an offset of 2 deviations both above and below the predicted values (the CDEFs upper and lower). Vertical lines indicated potential failures are graphed via the \s-1TICK\s0 graph element, which converts non-zero values in an \fB\s-1RRA\s0\fR into tick marks. Here an axis-fraction argument of 1.0 means the tick marks span the entire y\-axis, and hence become vertical lines on the graph. .PP The choice of 2 deviations (a scaling factor) matches the default used internally by the \s-1FAILURES\s0 \fB\s-1RRA\s0\fR. If the internal value is changed (see rrdtune), this graphing command should be changed to be consistent. .PP \fIA note on data reduction:\fR .IX Subsection "A note on data reduction:" .PP The \fBrrdtool\fR \fIgraph\fR command is designed to plot data at a specified temporal resolution, regardless of the actually resolution of the data in the \s-1RRD\s0 file. This can present a problem for the specialized consolidation functions which maintain a one-to-one mapping between primary data points and consolidated data points. If a graph insists on viewing the contents of these \fBRRAs\fR on a coarser temporal scale, the \fIgraph\fR command tries to do something intelligent, but the confidence bands and failures no longer have the same meaning and may be misleading. .SH "SEE ALSO" .IX Header "SEE ALSO" rrdgraph gives an overview of how \fBrrdtool graph\fR works. rrdgraph_data describes \fB\s-1DEF\s0\fR,\fB\s-1CDEF\s0\fR and \fB\s-1VDEF\s0\fR in detail. rrdgraph_rpn describes the \fB\s-1RPN\s0\fR language used in the \fBxDEF\fR statements. rrdgraph_graph page describes all the graph and print functions. .SH "AUTHOR" .IX Header "AUTHOR" Program by Tobias Oetiker .PP This manual page by Alex van den Bogaerdt with corrections and/or additions by several people rrdtool-1.4.7/doc/rrddump.10000644000175300017510000001472211707501546012455 00000000000000.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.07) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "RRDDUMP 1" .TH RRDDUMP 1 "2009-10-14" "1.4.7" "rrdtool" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" rrddump \- dump the contents of an RRD to XML format .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBrrdtool\fR \fBdump\fR \fIfilename.rrd\fR [\fIfilename.xml\fR] [\fB\-\-header\fR|\fB\-h\fR\ {none,xsd,dtd}] [\fB\-\-no\-header\fR] [\fB\-\-daemon\fR\ \fIaddress\fR] >\ \fIfilename.xml\fR .SH "DESCRIPTION" .IX Header "DESCRIPTION" The \fBdump\fR function writes the contents of an \fB\s-1RRD\s0\fR in human readable (?) \s-1XML\s0 format to a file or to stdout. This format can be read by rrdrestore. Together they allow you to transfer your files from one computer architecture to another as well to manipulate the contents of an \fB\s-1RRD\s0\fR file in a somewhat more convenient manner. .IP "\fIfilename.rrd\fR" 8 .IX Item "filename.rrd" The name of the \fB\s-1RRD\s0\fR you want to dump. .IP "\fIfilename.xml\fR" 8 .IX Item "filename.xml" The (optional) filename that you want to write the \s-1XML\s0 output to. If not specified, the \s-1XML\s0 will be printed to stdout. .IP "\fB\-\-header\fR|\fB\-h\fR {none,xsd,dtd}" 8 .IX Item "--header|-h {none,xsd,dtd}" By default RRDtool will add a dtd header to the xml file. Here you can customize this to and xsd header or no header at all. .IP "\fB\-\-no\-header\fR" 8 .IX Item "--no-header" A shortcut for \-\-header=none. .Sp If you want to restore the dump with RRDtool 1.2 you should use the \&\-\-no\-header option since 1.2 can not deal with xml headers. .IP "\fB\-\-daemon\fR \fIaddress\fR" 8 .IX Item "--daemon address" Address of the rrdcached daemon. If specified, a \f(CW\*(C`flush\*(C'\fR command is sent to the server before reading the \s-1RRD\s0 files. This allows \fBrrdtool\fR to return fresh data even if the daemon is configured to cache values for a long time. For a list of accepted formats, see the \fB\-l\fR option in the rrdcached manual. .Sp .Vb 1 \& rrdtool dump \-\-daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd .Ve .SH "EXAMPLES" .IX Header "EXAMPLES" To transfer an \s-1RRD\s0 between architectures, follow these steps: .IP "1." 4 On the same system where the \s-1RRD\s0 was created, use \fBrrdtool\fR \fBdump\fR to export the data to \s-1XML\s0 format. .IP "2." 4 Transfer the \s-1XML\s0 dump to the target system. .IP "3." 4 Run \fBrrdtool\fR \fBrestore\fR to create a new \s-1RRD\s0 from the \s-1XML\s0 dump. See \&\fBrrdrestore\fR for details. .SH "ENVIRONMENT VARIABLES" .IX Header "ENVIRONMENT VARIABLES" The following environment variables may be used to change the behavior of \&\f(CW\*(C`rrdtool\ dump\*(C'\fR: .IP "\fB\s-1RRDCACHED_ADDRESS\s0\fR" 4 .IX Item "RRDCACHED_ADDRESS" If this environment variable is set it will have the same effect as specifying the \f(CW\*(C`\-\-daemon\*(C'\fR option on the command line. If both are present, the command line argument takes precedence. .SH "AUTHOR" .IX Header "AUTHOR" Tobias Oetiker rrdtool-1.4.7/doc/bin_dec_hex.10000644000175300017510000004300411707501545013221 00000000000000.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.07) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "BIN_DEC_HEX 1" .TH BIN_DEC_HEX 1 "2009-06-10" "1.4.7" "rrdtool" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" bin_dec_hex \- How to use binary, decimal, and hexadecimal notation. .SH "DESCRIPTION" .IX Header "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 to the left. The digit 0 is only shown if it is the only symbol in the sequence, or if it is not the first one. .PP If this sounds cryptic to you, this is what I've just said in numbers: .PP .Vb 10 \& 0 \& 1 \& 2 \& 3 \& 4 \& 5 \& 6 \& 7 \& 8 \& 9 \& 10 \& 11 \& 12 \& 13 .Ve .PP and so on. .PP Each time the digit nine is incremented, it is reset to 0 and the position before (to the left) is incremented (from 0 to 1). Then number 9 can be seen as \*(L"00009\*(R" and when we should increment 9, we reset it to zero and increment the digit just before the 9 so the number becomes \*(L"00010\*(R". Leading zeros we don't write except if it is the only digit (number 0). And of course, we write zeros if they occur anywhere inside or at the end of a number: .PP .Vb 1 \& "00010" \-> " 0010" \-> " 010" \-> " 10", but not " 1 ". .Ve .PP This was pretty basic, you already knew this. Why did I tell it? Well, computers usually do not represent numbers with 10 different digits. They only use two different symbols, namely \*(L"0\*(R" and \*(L"1\*(R". Apply the same rules to this set of digits and you get the binary numbering system: .PP .Vb 10 \& 0 \& 1 \& 10 \& 11 \& 100 \& 101 \& 110 \& 111 \& 1000 \& 1001 \& 1010 \& 1011 \& 1100 \& 1101 .Ve .PP and so on. .PP 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 as in the first list, we just used 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 explicitly specify the numbering system used, we implicitly use the decimal system. If we want to use any other numbering system, we'll have to make that clear. There are a few widely adopted methods to do so. One common form is to write 1010(2) which means that you wrote down a number in its binary representation. It is the number ten. If you would write 1010 without specifying the base, the number is interpreted as one thousand and ten using base 10. .PP In books, another form is common. It uses subscripts (little characters, 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 by a little two just behind it. .PP As the numbering system used is also called the base, we talk of the number 1100 base 2, the number 12 base 10. .PP Within the binary system, it is common to write leading zeros. The numbers are written down in series of four, eight or sixteen depending on the context. .PP We can use the binary form when talking to computers (...programming...), but the numbers will have large representations. The number 65'535 (often in the decimal system a ' is used to separate blocks of three digits for readability) would be written down as 1111111111111111(2) which is 16 times the digit 1. This is difficult and prone to errors. Therefore, we usually would use another base, called hexadecimal. It uses 16 different symbols. First the symbols from the decimal system are used, thereafter we continue with 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 easily (and back). .PP There is yet another system in use, called the octal system. This was more common in the old days, but is not used very often anymore. As you might find it in use sometimes, you should get used to it and we'll show it below. It's the same story as with the other representations, but with eight different symbols. .PP .Vb 4 \& 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 .Ve .PP 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 practical 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: .PP .Vb 2 \& (2) (8) (10) (16) \& 11111111 377 255 FF .Ve .PP 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: take four digits from the right and make a hexadecimal digit from it (see the table above). Repeat this until there are no more digits. And the other way around: Take a hexadecimal number. For each digit, write down its binary equivalent. .PP Computers (or rather the parsers running on them) would have a hard time converting a number like 1234(16). Therefore hexadecimal numbers are specified with a prefix. This prefix depends on the language you're writing in. Some of the prefixes are \*(L"0x\*(R" for C, \*(L"$\*(R" for Pascal, \*(L"#\*(R" for \s-1HTML\s0. 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 \*(L"0x\*(R" for hexadecimal, \*(L"%\*(R" for binary and \*(L"0\*(R" for octal. The following numbers are all the same, just their representation (base) is different: 021 0x11 17 \f(CW%00010001\fR .PP To do arithmetics and conversions you need to understand one more thing. It is something you already know but perhaps you do not \*(L"see\*(R" it yet: .PP If you write down 1234, (no prefix, so it is decimal) you are talking about the number one thousand, two hundred and thirty four. In sort of a formula: .PP .Vb 4 \& 1 * 1000 = 1000 \& 2 * 100 = 200 \& 3 * 10 = 30 \& 4 * 1 = 4 .Ve .PP This can also be written as: .PP .Vb 4 \& 1 * 10^3 \& 2 * 10^2 \& 3 * 10^1 \& 4 * 10^0 .Ve .PP where ^ means \*(L"to the power of\*(R". .PP We are using the base 10, and the positions 0,1,2 and 3. The right-most position should \s-1NOT\s0 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. .PP It is the same in all other representations: .PP 0x1234 will be .PP .Vb 4 \& 1 * 16^3 \& 2 * 16^2 \& 3 * 16^1 \& 4 * 16^0 .Ve .PP 01234 would be .PP .Vb 4 \& 1 * 8^3 \& 2 * 8^2 \& 3 * 8^1 \& 4 * 8^0 .Ve .PP This example can not be done for binary as that system only uses two symbols. Another example: .PP \&\f(CW%1010\fR would be .PP .Vb 4 \& 1 * 2^3 \& 0 * 2^2 \& 1 * 2^1 \& 0 * 2^0 .Ve .PP It would have been easier to convert it to its hexadecimal form and just translate \f(CW%1010\fR 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. .PP To convert a decimal number into a hexadecimal you could use the next method. It will take some time to be able to do the estimates, but it will be easier when you use the system more frequently. We'll look at yet another way afterwards. .PP First you need to know how many positions will be used in the other system. To do so, you need to know the maximum numbers you'll be using. Well, that's not as hard as it looks. In decimal, the maximum number that you can form with two digits is \*(L"99\*(R". The maximum for three: \*(L"999\*(R". 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. .PP This can be done for hexadecimal as well: .PP .Vb 4 \& 16^4 = 0x10000 = 65536 \& 16^3 = 0x1000 = 4096 \& 16^2 = 0x100 = 256 \& 16^1 = 0x10 = 16 .Ve .PP If a number is smaller than 65'536 it will fit in four positions. If the number is bigger than 4'095, you must use position 4. How many times you can subtract 4'096 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. .PP Let's try with 41'029. 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 41'029 ten times without going below zero. The left-most digit will therefore be \*(L"A\*(R", so we have 0xA????. The number is reduced to 41'029 \- 10*4'096 = 41'029\-40'960 = 69. 69 is smaller than 16^3 but not bigger than 16^2\-1. The second digit is therefore \*(L"0\*(R" and we now have 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 \*(L"4\*(R" to get 0xA04?. Subtract 64 from 69 (69 \- 4*16) and the last digit is 5 \-\-> 0xA045. .PP The other method builds up the number from the right. Let's try 41'029 again. Divide by 16 and do not use fractions (only whole numbers). .PP .Vb 4 \& 41\*(Aq029 / 16 is 2\*(Aq564 with a remainder of 5. Write down 5. \& 2\*(Aq564 / 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. .Ve .PP Which method to use is up to you. Use whatever works for you. 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 to one I am familiar with, then I will use the first method (like 32'770 which is into 32'768 + 2 and I just know that it is 0x8000 + 0x2 = 0x8002). .PP 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 easily if you should write down a zero or a one: if you divide by two the remainder will be zero if it is an even number and one if it is an odd number: .PP .Vb 10 \& 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 .Ve .PP Write down the results from right to left: \f(CW%1010000001000101\fR .PP Group by four: .PP .Vb 4 \& %1010000001000101 \& %101000000100 0101 \& %10100000 0100 0101 \& %1010 0000 0100 0101 .Ve .PP Convert into hexadecimal: 0xA045 .PP Group \f(CW%1010000001000101\fR by three and convert into octal: .PP .Vb 8 \& %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 .Ve .PP 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 directly in the base that is used. In any representation it is the same, add the numbers on the right, write down the right-most digit from the result, remember the other digits and use them in the next round. Continue with the second digit from the right and so on: .PP .Vb 1 \& %1010 + %0111 \-\-> 10 + 7 \-\-> 17 \-\-> %00010001 .Ve .PP will become .PP .Vb 10 \& %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 .Ve .PP For low values, try to do the calculations yourself, then check them with a calculator. The more you do the calculations yourself, the more you'll find that you didn't make mistakes. In the end, you'll do calculi in other bases as easily as you do them in decimal. .PP 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 \*(L"bc\*(R" which is short for Binary Calculator. It calculates not only in decimal, but in all bases you'll ever want to use (among them Binary). .PP 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. .SH "AUTHOR" .IX Header "AUTHOR" I hope you enjoyed the examples and their descriptions. If you do, help other people by pointing them to this document when they are asking basic questions. They will not only get their answer, but at the same time learn a whole lot more. .PP Alex van den Bogaerdt rrdtool-1.4.7/doc/RRDp.html0000644000175300017510000001201011707501545012376 00000000000000 RRDp

NAME

RRDp - Attach RRDtool from within a perl script via a set of pipes;


SYNOPSIS

use RRDp

RRDp::start path to RRDtool executable

RRDp::cmd rrdtool commandline

$answer = RRD::read

$status = RRD::end

$RRDp::user, $RRDp::sys, $RRDp::real, $RRDp::error_mode, $RRDp::error


DESCRIPTION

With this module you can safely communicate with the RRDtool.

After every RRDp::cmd you have to issue an RRDp::read command to get RRDtools 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, RRDp::read will return an undefined variable.

If you import the PERFORMANCE variables into your namespace, you can access RRDtool's internal performance measurements.

use RRDp

Load the RRDp::pipe module.

RRDp::start path to RRDtool executable

start RRDtool. The argument must be the path to the RRDtool executable

RRDp::cmd rrdtool commandline

pass commands on to RRDtool. Check the RRDtool documentation for more info on the RRDtool commands.

Note: Due to design limitations, RRDp::cmd does not support the graph - command - use graphv - instead.

$answer = RRDp::read

read RRDtool's 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 = RRDp::end

terminates RRDtool and returns RRDtool's status ...

$RRDp::user, $RRDp::sys, $RRDp::real

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.

$RRDp::error_mode and $RRDp::error

If you set the variable $RRDp::error_mode to the value 'catch' before you run RRDp::read a potential ERROR message will not cause the program to abort but will be returned in this variable. If no error occurs the variable will be empty.

 $RRDp::error_mode = 'catch';
 RRDp::cmd qw(info file.rrd);
 print $RRDp::error if $RRDp::error;


EXAMPLE

 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);


SEE ALSO

For more information on how to use RRDtool, check the manpages.


AUTHOR

Tobias Oetiker <tobi@oetiker.ch>

rrdtool-1.4.7/doc/RRDs.html0000644000175300017510000001351411707501545012413 00000000000000 RRDs

NAME

RRDs - Access RRDtool as a shared module


SYNOPSIS

  use RRDs;
  RRDs::error
  RRDs::last ...
  RRDs::info ...
  RRDs::create ...
  RRDs::update ...
  RRDs::updatev ...
  RRDs::graph ...
  RRDs::fetch ...
  RRDs::tune ...
  RRDs::times(start, end)
  RRDs::dump ...
  RRDs::restore ...
  RRDs::flushcached ...


DESCRIPTION

Calling Sequence

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 command line 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.

The RRDs::times function takes two parameters: a "start" and "end" time. These should be specified in the AT-STYLE TIME SPECIFICATION format used by RRDtool. See the rrdfetch documentation for a detailed explanation on how to specify time.

Error Handling

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;

Return Values

The functions RRDs::last, RRDs::graph, RRDs::info, RRDs::fetch and RRDs::times return their findings.

RRDs::last returns a single INTEGER representing the last update time.

 $lastupdate = RRDs::last ...

RRDs::graph returns an ARRAY containing the x-size and y-size of the created image and a pointer to an array with the results of the PRINT arguments.

 ($result_arr,$xsize,$ysize) = RRDs::graph ...
 print "Imagesize: ${xsize}x${ysize}\n";
 print "Averages: ", (join ", ", @$averages);

RRDs::info 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";
 }

RRDs::graphv takes the same parameters as RRDs::graph but it returns a pointer to hash. The hash returned contains meta information about the graph. Like its size as well as the position of the graph area on the image. When calling with and empty filename than the contents of the graph will be returned in the hash as well (key 'image').

RRDs::updatev also returns a pointer to hash. The keys of the hash are concatenated strings of a timestamp, RRA index, and data source name for each consolidated data point (CDP) written to disk as a result of the current update call. The hash values are CDP values.

RRDs::fetch 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";
  for my $line (@$data) {
    print "  ", scalar localtime($start), " ($start) ";
    $start += $step;
    for my $val (@$line) {
      printf "%12.1f ", $val;
    }
    print "\n";
  }

RRDs::times returns two integers which are the number of seconds since epoch (1970-01-01) for the supplied "start" and "end" arguments, respectively.

See the examples directory for more ways to use this extension.


NOTE

If you are manipulating the TZ variable you should also call the POSIX function tzset(3) to initialize all internal state of the library for properly operating in the timezone of your choice.

 use POSIX qw(tzset);
 $ENV{TZ} = 'CET';   
 POSIX::tzset();


AUTHOR

Tobias Oetiker <tobi@oetiker.ch>

rrdtool-1.4.7/doc/librrd.html0000644000175300017510000001537211707501545013063 00000000000000 librrd


NAME

librrd - RRD library functions


DESCRIPTION

librrd contains most of the functionality in RRDTool. The command line utilities and language bindings are often just wrappers around the code contained in librrd.

This manual page documents the librrd API.

NOTE: This document is a work in progress, and should be considered incomplete as long as this warning persists. For more information about the librrd functions, always consult the source code.


CORE FUNCTIONS

rrd_dump_cb_r(char *filename, int opt_header, rrd_output_callback_t cb, void *user)

In some situations it is necessary to get the output of rrd_dump without writing it to a file or the standard output. In such cases an application can ask rrd_dump_cb_r to call an user-defined function each time there is output to be stored somewhere. This can be used, to e.g. directly feed an XML parser with the dumped output or transfer the resulting string in memory.

The arguments for rrd_dump_cb_r are the same as for rrd_dump_opt_r except that the output filename parameter is replaced by the user-defined callback function and an additional parameter for the callback function that is passed untouched, i.e. to store information about the callback state needed for the user-defined callback to function properly.

Recent versions of rrd_dump_opt_r internally use this callback mechanism to write their output to the file provided by the user.

    size_t rrd_dump_opt_cb_fileout(
        const void *data,
        size_t len,
        void *user)
    {
        return fwrite(data, 1, len, (FILE *)user);
    }

The associated call for rrd_dump_cb_r looks like

    res = rrd_dump_cb_r(filename, opt_header,
        rrd_dump_opt_cb_fileout, (void *)out_file);

where the last parameter specifies the file handle rrd_dump_opt_cb_fileout should write to. There's no specific condition for the callback to detect when it is called for the first time, nor for the last time. If you require this for initialization and cleanup you should do those tasks before and after calling rrd_dump_cr_r respectively.


UTILITY FUNCTIONS

rrd_random()

Generates random numbers just like random(). This further ensures that the random number generator is seeded exactly once per process.

rrd_add_ptr(void ***dest, size_t *dest_size, void *src)

Dynamically resize the array pointed to by dest. dest_size is a pointer to the current size of dest. Upon successful realloc(), the dest_size is incremented by 1 and the src pointer is stored at the end of the new dest. Returns 1 on success, 0 on failure.

    type **arr = NULL;
    type *elem = "whatever";
    size_t arr_size = 0;
    if (!rrd_add_ptr(&arr, &arr_size, elem))
        handle_failure();
rrd_add_strdup(char ***dest, size_t *dest_size, char *src)

Like rrd_add_ptr, except adds a strdup of the source string.

    char **arr = NULL;
    size_t arr_size = NULL;
    char *str  = "example text";
    if (!rrd_add_strdup(&arr, &arr_size, str))
        handle_failure();
rrd_free_ptrs(void ***src, size_t *cnt)

Free an array of pointers allocated by rrd_add_ptr or rrd_add_strdup. Also frees the array pointer itself. On return, the source pointer will be NULL and the count will be zero.

    /* created as above */
    rrd_free_ptrs(&arr, &arr_size);
    /* here, arr == NULL && arr_size == 0 */
rrd_mkdir_p(const char *pathname, mode_t mode)

Create the directory named pathname including all of its parent directories (similar to mkdir -p on the command line - see mkdir(1) for more information). The argument mode specifies the permissions to use. It is modified by the process's umask. See mkdir(2) for more details.

The function returns 0 on success, a negative value else. In case of an error, errno is set accordingly. Aside from the errors documented in mkdir(2), the function may fail with the following errors:

EINVAL

pathname is NULL or the empty string.

ENOMEM

Insufficient memory was available.

any error returned by stat(2)

In contrast to mkdir(2), the function does not fail if pathname already exists and is a directory.


AUTHOR

RRD Contributors <rrd-developers@lists.oetiker.ch>

rrdtool-1.4.7/doc/rrdtutorial.html0000644000175300017510000016177011707501545014164 00000000000000 rrdtutorial


NAME

rrdtutorial - Alex van den Bogaerdt's RRDtool tutorial


DESCRIPTION

RRDtool is written by Tobias Oetiker <tobi@oetiker.ch> with contributions from many people all around the world. This document is written by Alex van den Bogaerdt <alex@vandenbogaerdt.nl> to help you understand what RRDtool is and what it can do for you.

The documentation provided with RRDtool can be too technical for some people. This tutorial is here to help you understand the basics of RRDtool. It should prepare you to read the documentation yourself. It also explains the general things about statistics with a focus on networking.


TUTORIAL

Important

Please don't skip ahead in this document! The first part of this document explains the basics and may be boring. But if you don't understand the basics, the examples will not be as meaningful to you.

Sometimes things change. This example used to provide numbers like "0.04" in stead of "4.00000e-02". Those are really the same numbers, just written down differently. Don't be alarmed if a future version of rrdtool displays a slightly different form of output. The examples in this document are correct for version 1.2.0 of RRDtool.

Also, sometimes bugs do occur. They may also influence the outcome of the examples. Example speed4.png was suffering from this (the handling of unknown data in an if-statement was wrong). Normal data will be just fine (a bug in rrdtool wouldn't last long) but special cases like NaN, INF and so on may last a bit longer. Try another version if you can, or just live with it.

I fixed the speed4.png example (and added a note). There may be other examples which suffer from the same or a similar bug. Try to fix it yourself, which is a great excercise. But please do not submit your result as a fix to the source of this document. Discuss it on the user's list, or write to me.

What is RRDtool?

RRDtool refers to Round Robin Database tool. Round robin is a technique that works with a fixed amount of data, and a pointer to the current element. Think of a circle with some dots plotted on the edge. These dots are the places where data can be stored. Draw an arrow from the center of the circle to one of the dots; this is the pointer. When the current data is read or written, the pointer moves to the next element. As we are on a circle there is neither a beginning nor an end, you can go on and on and on. After a while, all the available places will be used and the process automatically reuses old locations. This way, the dataset will not grow in size and therefore requires no maintenance. RRDtool works with Round Robin Databases (RRDs). It stores and retrieves data from them.

What data can be put into an RRD?

You name it, it will probably fit as long as it is some sort of time-series data. This means you have to be able to measure some value at several points in time and provide this information to RRDtool. If you can do this, RRDtool will be able to store it. The values must be numerical but don't have to be integers, as is the case with MRTG (the next section will give more details on this more specialized application).

Many examples below talk about SNMP which is an acronym for Simple Network Management Protocol. "Simple" refers to the protocol. It does not mean it is simple to manage or monitor a network. After working your way through this document, you should know enough to be able to understand what people are talking about. For now, just realize that SNMP can be used to query devices for the values of counters they keep. It is the value from those counters that we want to store in the RRD.

What can I do with this tool?

RRDtool originated from MRTG (Multi Router Traffic Grapher). MRTG started as a tiny little script for graphing the use of a university's connection to the Internet. MRTG was later (ab-)used as a tool for graphing other data sources including temperature, speed, voltage, number of printouts and the like.

Most likely you will start to use RRDtool to store and process data collected via SNMP. The data will most likely be bytes (or bits) transferred from and to a network or a computer. But it can also be used to display tidal waves, solar radiation, power consumption, number of visitors at an exhibition, noise levels near an airport, temperature on your favorite holiday location, temperature in the fridge and whatever your imagination can come up with.

You only need a sensor to measure the data and be able to feed the numbers into RRDtool. RRDtool then lets you create a database, store data in it, retrieve that data and create graphs in PNG format for display on a web browser. Those PNG images are dependent on the data you collected and could be, for instance, an overview of the average network usage, or the peaks that occurred.

What if I still have problems after reading this document?

First of all: read it again! You may have missed something. If you are unable to compile the sources and you have a fairly common OS, it will probably not be the fault of RRDtool. There may be pre-compiled versions around on the Internet. If they come from trusted sources, get one of those.

If on the other hand the program works but does not give you the expected results, it will be a problem with configuring it. Review your configuration and compare it with the examples that follow.

There is a mailing list and an archive of it. Read the list for a few weeks and search the archive. It is considered rude to just ask a question without searching the archives: your problem may already have been solved for somebody else! This is true for most, if not all, mailing lists and not only for this particular one. Look in the documentation that came with RRDtool for the location and usage of the list.

I suggest you take a moment to subscribe to the mailing list right now by sending an email to <rrd-users-request@lists.oetiker.ch> with a subject of "subscribe". If you ever want to leave this list, just write an email to the same address but now with a subject of "unsubscribe".

How will you help me?

By giving you some detailed descriptions with detailed examples. I assume that following the instructions in the order presented will give you enough knowledge of RRDtool to experiment for yourself. If it doesn't work the first time, don't give up. Reread the stuff that you did understand, you may have missed something.

By following the examples you get some hands-on experience and, even more important, some background information of how it works.

You will need to know something about hexadecimal numbers. If you don't then start with reading the bin_dec_hex manpage before you continue here.

Your first Round Robin Database

In my opinion the best way to learn something is to actually do it. Why not start right now? We will create a database, put some values in it and extract this data again. Your output should be the same as the output that is included in this document.

We will start with some easy stuff and compare a car with a router, or compare kilometers (miles if you wish) with bits and bytes. It's all the same: some number over some time.

Assume we have a device that transfers bytes to and from the Internet. This device keeps a counter that starts at zero when it is turned on, increasing with every byte that is transferred. This counter will probably have a maximum value. If this value is reached and an extra byte is counted, the counter starts over at zero. This is the same as many counters in the world such as the mileage counter in a car.

Most discussions about networking talk about bits per second so lets get used to that right away. Assume a byte is eight bits and start to think in bits not bytes. The counter, however, still counts bytes! In the SNMP world most of the counters are 32 bits. That means they are counting from 0 to 4294967295. We will use these values in the examples. The device, when asked, returns the current value of the counter. We know the time that has passes since we last asked so we now know how many bytes have been transferred ***on average*** per second. This is not very hard to calculate. First in words, then in calculations:

  1. Take the current counter, subtract the previous value from it.

  2. Do the same with the current time and the previous time (in seconds).

  3. Divide the outcome of (1) by the outcome of (2), the result is the amount of bytes per second. Multiply by eight to get the number of bits per second (bps).

  bps = (counter_now - counter_before) / (time_now - time_before) * 8

For some people it may help to translate this to an automobile example. Do not try this example, and if you do, don't blame me for the results!

People who are not used to think in kilometers per hour can translate most into miles per hour by dividing km by 1.6 (close enough). I will use the following abbreviations:

 m:    meter
 km:   kilometer (= 1000 meters).
 h:    hour
 s:    second
 km/h: kilometers per hour
 m/s:  meters per second

You are driving a car. At 12:05 you read the counter in the dashboard and it tells you that the car has moved 12345 km until that moment. At 12:10 you look again, it reads 12357 km. This means you have traveled 12 km in five minutes. A scientist would translate that into meters per second and this makes a nice comparison toward the problem of (bytes per five minutes) versus (bits per second).

We traveled 12 kilometers which is 12000 meters. We did that in five minutes or 300 seconds. Our speed is 12000m / 300s or 40 m/s.

We could also calculate the speed in km/h: 12 times 5 minutes is an hour, so we have to multiply 12 km by 12 to get 144 km/h. For our native English speaking friends: that's 90 mph so don't try this example at home or where I live :)

Remember: these numbers are averages only. There is no way to figure out from the numbers, if you drove at a constant speed. There is an example later on in this tutorial that explains this.

I hope you understand that there is no difference in calculating m/s or bps; only the way we collect the data is different. Even the k from kilo is the same as in networking terms k also means 1000.

We will now create a database where we can keep all these interesting numbers. The method used to start the program may differ slightly from OS to OS, but I assume you can figure it out if it works different on your's. Make sure you do not overwrite any file on your system when executing the following command and type the whole line as one long line (I had to split it for readability) and skip all of the '\' characters.

   rrdtool create test.rrd             \
            --start 920804400          \
            DS:speed:COUNTER:600:U:U   \
            RRA:AVERAGE:0.5:1:24       \
            RRA:AVERAGE:0.5:6:10

(So enter: rrdtool create test.rrd --start 920804400 DS ...)

What has been created?

We created the round robin database called test (test.rrd) which starts at noon the day I started writing this document, 7th of March, 1999 (this date translates to 920804400 seconds as explained below). Our database holds one data source (DS) named "speed" that represents a counter. This counter is read every five minutes (this is the default therefore you don't have to put --step=300). In the same database two round robin archives (RRAs) are kept, one averages the data every time it is read (e.g., there's nothing to average) and keeps 24 samples (24 times 5 minutes is 2 hours). The other averages 6 values (half hour) and contains 10 such averages (e.g. 5 hours).

RRDtool works with special time stamps coming from the UNIX world. This time stamp is the number of seconds that passed since January 1st 1970 UTC. The time stamp value is translated into local time and it will therefore look different for different time zones.

Chances are that you are not in the same part of the world as I am. This means your time zone is different. In all examples where I talk about time, the hours may be wrong for you. This has little effect on the results of the examples, just correct the hours while reading. As an example: where I will see "12:05" the UK folks will see "11:05".

We now have to fill our database with some numbers. We'll pretend to have read the following numbers:

 12:05  12345 km
 12:10  12357 km
 12:15  12363 km
 12:20  12363 km
 12:25  12363 km
 12:30  12373 km
 12:35  12383 km
 12:40  12393 km
 12:45  12399 km
 12:50  12405 km
 12:55  12411 km
 13:00  12415 km
 13:05  12420 km
 13:10  12422 km
 13:15  12423 km

We fill the database as follows:

 rrdtool update test.rrd 920804700:12345 920805000:12357 920805300:12363
 rrdtool update test.rrd 920805600:12363 920805900:12363 920806200:12373
 rrdtool update test.rrd 920806500:12383 920806800:12393 920807100:12399
 rrdtool update test.rrd 920807400:12405 920807700:12411 920808000:12415
 rrdtool update test.rrd 920808300:12420 920808600:12422 920808900:12423

This reads: update our test database with the following numbers

 time 920804700, value 12345
 time 920805000, value 12357

etcetera.

As you can see, it is possible to feed more than one value into the database in one command. I had to stop at three for readability but the real maximum per line is OS dependent.

We can now retrieve the data from our database using "rrdtool fetch":

 rrdtool fetch test.rrd AVERAGE --start 920804400 --end 920809200

It should return the following output:

                          speed
 920804700: nan
 920805000: 4.0000000000e-02
 920805300: 2.0000000000e-02
 920805600: 0.0000000000e+00
 920805900: 0.0000000000e+00
 920806200: 3.3333333333e-02
 920806500: 3.3333333333e-02
 920806800: 3.3333333333e-02
 920807100: 2.0000000000e-02
 920807400: 2.0000000000e-02
 920807700: 2.0000000000e-02
 920808000: 1.3333333333e-02
 920808300: 1.6666666667e-02
 920808600: 6.6666666667e-03
 920808900: 3.3333333333e-03
 920809200: nan
 920809500: nan

Note that you might get more rows than you expect. The reason for this is that you ask for a time range that ends on 920809200. The number that is written behind 920809200: in the list above covers the time range from 920808900 to 920809200, EXCLUDING 920809200. Hence to be on the sure side, you receive the entry from 920809200 to 920809500 as well since it INCLUDES 920809200. You may also see "NaN" instead of "nan" this is OS dependent. "NaN" stands for "Not A Number". If your OS writes "U" or "UNKN" or something similar that's okay. If something else is wrong, it will probably be due to an error you made (assuming that my tutorial is correct of course :-). In that case: delete the database and try again.

The meaning of the above output will become clear below.

Time to create some graphics

Try the following command:

 rrdtool graph speed.png                                 \
         --start 920804400 --end 920808000               \
         DEF:myspeed=test.rrd:speed:AVERAGE              \
         LINE2:myspeed#FF0000

This will create speed.png which starts at 12:00 and ends at 13:00. There is a definition of a variable called myspeed, using the data from RRA "speed" out of database "test.rrd". The line drawn is 2 pixels high and represents the variable myspeed. The color is red (specified by its rgb-representation, see below).

You'll notice that the start of the graph is not at 12:00 but at 12:05. This is because we have insufficient data to tell the average before that time. This will only happen when you miss some samples, this will not happen a lot, hopefully.

If this has worked: congratulations! If not, check what went wrong.

The colors are built up from red, green and blue. For each of the components, you specify how much to use in hexadecimal where 00 means not included and FF means fully included. The "color" white is a mixture of red, green and blue: FFFFFF The "color" black is all colors off: 000000

   red     #FF0000
   green   #00FF00
   blue    #0000FF
   magenta #FF00FF     (mixed red with blue)
   gray    #555555     (one third of all components)

Additionally you can (with a recent RRDtool) add an alpha channel (transparency). The default will be "FF" which means non-transparent.

The PNG you just created can be displayed using your favorite image viewer. Web browsers will display the PNG via the URL "file:///the/path/to/speed.png"

Graphics with some math

When looking at the image, you notice that the horizontal axis is labeled 12:10, 12:20, 12:30, 12:40 and 12:50. Sometimes a label doesn't fit (12:00 and 13:00 would be likely candidates) so they are skipped.

The vertical axis displays the range we entered. We provided kilometers and when divided by 300 seconds, we get very small numbers. To be exact, the first value was 12 (12357-12345) and divided by 300 this makes 0.04, which is displayed by RRDtool as "40 m" meaning "40/1000". The "m" (milli) has nothing to do with meters (also m), kilometers or millimeters! RRDtool doesn't know about the physical units of our data, it just works with dimensionless numbers.

If we had measured our distances in meters, this would have been (12357000-12345000)/300 = 12000/300 = 40.

As most people have a better feel for numbers in this range, we'll correct that. We could recreate our database and store the correct data, but there is a better way: we do some calculations while creating the png file!

   rrdtool graph speed2.png                           \
      --start 920804400 --end 920808000               \
      --vertical-label m/s                            \
      DEF:myspeed=test.rrd:speed:AVERAGE              \
      CDEF:realspeed=myspeed,1000,\*                  \
      LINE2:realspeed#FF0000

Note: I need to escape the multiplication operator * with a backslash. If I don't, the operating system may interpret it and use it for file name expansion. You could also place the line within quotation marks like so:

      "CDEF:realspeed=myspeed,1000,*"                  \

It boils down to: it is RRDtool which should see *, not your shell. And it is your shell interpreting \, not RRDtool. You may need to adjust examples accordingly if you happen to use an operating system or shell which behaves differently.

After viewing this PNG, you notice the "m" (milli) has disappeared. This it what the correct result would be. Also, a label has been added to the image. Apart from the things mentioned above, the PNG should look the same.

The calculations are specified in the CDEF part above and are in Reverse Polish Notation ("RPN"). What we requested RRDtool to do is: "take the data source myspeed and the number 1000; multiply those". Don't bother with RPN yet, it will be explained later on in more detail. Also, you may want to read my tutorial on CDEFs and Steve Rader's tutorial on RPN. But first finish this tutorial.

Hang on! If we can multiply values with 1000, it should also be possible to display kilometers per hour from the same data!

To change a value that is measured in meters per second:

 Calculate meters per hour:     value * 3600
 Calculate kilometers per hour: value / 1000
 Together this makes:           value * (3600/1000) or value * 3.6

In our example database we made a mistake and we need to compensate for this by multiplying with 1000. Applying that correction:

 value * 3.6  * 1000 == value * 3600

Now let's create this PNG, and add some more magic ...

 rrdtool graph speed3.png                             \
      --start 920804400 --end 920808000               \
      --vertical-label km/h                           \
      DEF:myspeed=test.rrd:speed:AVERAGE              \
      "CDEF:kmh=myspeed,3600,*"                       \
      CDEF:fast=kmh,100,GT,kmh,0,IF                   \
      CDEF:good=kmh,100,GT,0,kmh,IF                   \
      HRULE:100#0000FF:"Maximum allowed"              \
      AREA:good#00FF00:"Good speed"                   \
      AREA:fast#FF0000:"Too fast"

Note: here we use another means to escape the * operator by enclosing the whole string in double quotes.

This graph looks much better. Speed is shown in km/h and there is even an extra line with the maximum allowed speed (on the road I travel on). I also changed the colors used to display speed and changed it from a line into an area.

The calculations are more complex now. For speed measurements within the speed limit they are:

   Check if kmh is greater than 100    ( kmh,100 ) GT
   If so, return 0, else kmh           ((( kmh,100 ) GT ), 0, kmh) IF

For values above the speed limit:

   Check if kmh is greater than 100    ( kmh,100 ) GT
   If so, return kmh, else return 0    ((( kmh,100) GT ), kmh, 0) IF

Graphics Magic

I like to believe there are virtually no limits to how RRDtool graph can manipulate data. I will not explain how it works, but look at the following PNG:

   rrdtool graph speed4.png                           \
      --start 920804400 --end 920808000               \
      --vertical-label km/h                           \
      DEF:myspeed=test.rrd:speed:AVERAGE              \
      CDEF:nonans=myspeed,UN,0,myspeed,IF             \
      CDEF:kmh=nonans,3600,*                          \
      CDEF:fast=kmh,100,GT,100,0,IF                   \
      CDEF:over=kmh,100,GT,kmh,100,-,0,IF             \
      CDEF:good=kmh,100,GT,0,kmh,IF                   \
      HRULE:100#0000FF:"Maximum allowed"              \
      AREA:good#00FF00:"Good speed"                   \
      AREA:fast#550000:"Too fast"                     \
      STACK:over#FF0000:"Over speed"

Remember the note in the beginning? I had to remove unknown data from this example. The 'nonans' CDEF is new, and the 6th line (which used to be the 5th line) used to read 'CDEF:kmh=myspeed,3600,*'

Let's create a quick and dirty HTML page to view the three PNGs:

   <HTML><HEAD><TITLE>Speed</TITLE></HEAD><BODY>
   <IMG src="speed2.png" alt="Speed in meters per second">
   <BR>
   <IMG src="speed3.png" alt="Speed in kilometers per hour">
   <BR>
   <IMG src="speed4.png" alt="Traveled too fast?">
   </BODY></HTML>

Name the file "speed.html" or similar, and look at it in your web browser.

Now, all you have to do is measure the values regularly and update the database. When you want to view the data, recreate the PNGs and make sure to refresh them in your browser. (Note: just clicking reload may not be enough, especially when proxies are involved. Try shift-reload or ctrl-F5).

Updates in Reality

We've already used the update command: it took one or more parameters in the form of "<time>:<value>". You'll be glad to know that you can specify the current time by filling in a "N" as the time. Or you could use the "time" function in Perl (the shortest example in this tutorial):

   perl -e 'print time, "\n" '

How to run a program on regular intervals is OS specific. But here is an example in pseudo code:

   - Get the value and put it in variable "$speed"
   - rrdtool update speed.rrd N:$speed

(do not try this with our test database, we'll use it in further examples)

This is all. Run the above script every five minutes. When you need to know what the graphs look like, run the examples above. You could put them in a script as well. After running that script, view the page index.html we created above.

Some words on SNMP

I can imagine very few people that will be able to get real data from their car every five minutes. All other people will have to settle for some other kind of counter. You could measure the number of pages printed by a printer, for example, the cups of coffee made by the coffee machine, a device that counts the electricity used, whatever. Any incrementing counter can be monitored and graphed using the stuff you learned so far. Later on we will also be able to monitor other types of values like temperature.

Many people interested in RRDtool will use the counter that keeps track of octets (bytes) transferred by a network device. So let's do just that next. We will start with a description of how to collect data.

Some people will make a remark that there are tools which can do this data collection for you. They are right! However, I feel it is important that you understand they are not necessary. When you have to determine why things went wrong you need to know how they work.

One tool used in the example has been talked about very briefly in the beginning of this document, it is called SNMP. It is a way of talking to networked equipment. The tool I use below is called "snmpget" and this is how it works:

   snmpget device password OID

or

   snmpget -v[version] -c[password] device OID

For device you substitute the name, or the IP address, of your device. For password you use the "community read string" as it is called in the SNMP world. For some devices the default of "public" might work, however this can be disabled, altered or protected for privacy and security reasons. Read the documentation that comes with your device or program.

Then there is this parameter, called OID, which means "object identifier".

When you start to learn about SNMP it looks very confusing. It isn't all that difficult when you look at the Management Information Base ("MIB"). It is an upside-down tree that describes data, with a single node as the root and from there a number of branches. These branches end up in another node, they branch out, etc. All the branches have a name and they form the path that we follow all the way down. The branches that we follow are named: iso, org, dod, internet, mgmt and mib-2. These names can also be written down as numbers and are 1 3 6 1 2 1.

   iso.org.dod.internet.mgmt.mib-2 (1.3.6.1.2.1)

There is a lot of confusion about the leading dot that some programs use. There is *no* leading dot in an OID. However, some programs can use the above part of OIDs as a default. To indicate the difference between abbreviated OIDs and full OIDs they need a leading dot when you specify the complete OID. Often those programs will leave out the default portion when returning the data to you. To make things worse, they have several default prefixes ...

Ok, lets continue to the start of our OID: we had 1.3.6.1.2.1 From there, we are especially interested in the branch "interfaces" which has number 2 (e.g., 1.3.6.1.2.1.2 or 1.3.6.1.2.1.interfaces).

First, we have to get some SNMP program. First look if there is a pre-compiled package available for your OS. This is the preferred way. If not, you will have to get the sources yourself and compile those. The Internet is full of sources, programs etc. Find information using a search engine or whatever you prefer.

Assume you got the program. First try to collect some data that is available on most systems. Remember: there is a short name for the part of the tree that interests us most in the world we live in!

I will give an example which can be used on Fedora Core 3. If it doesn't work for you, work your way through the manual of snmp and adapt the example to make it work.

   snmpget -v2c -c public myrouter system.sysDescr.0

The device should answer with a description of itself, perhaps an empty one. Until you got a valid answer from a device, perhaps using a different "password", or a different device, there is no point in continuing.

   snmpget -v2c -c public myrouter interfaces.ifNumber.0

Hopefully you get a number as a result, the number of interfaces. If so, you can carry on and try a different program called "snmpwalk".

   snmpwalk -v2c -c public myrouter interfaces.ifTable.ifEntry.ifDescr

If it returns with a list of interfaces, you're almost there. Here's an example: [user@host /home/alex]$ snmpwalk -v2c -c public cisco 2.2.1.2

   interfaces.ifTable.ifEntry.ifDescr.1 = "BRI0: B-Channel 1"
   interfaces.ifTable.ifEntry.ifDescr.2 = "BRI0: B-Channel 2"
   interfaces.ifTable.ifEntry.ifDescr.3 = "BRI0" Hex: 42 52 49 30
   interfaces.ifTable.ifEntry.ifDescr.4 = "Ethernet0"
   interfaces.ifTable.ifEntry.ifDescr.5 = "Loopback0"

On this cisco equipment, I would like to monitor the "Ethernet0" interface and from the above output I see that it is number four. I try:

   [user@host /home/alex]$ snmpget -v2c -c public cisco 2.2.1.10.4 2.2.1.16.4
   interfaces.ifTable.ifEntry.ifInOctets.4 = 2290729126
   interfaces.ifTable.ifEntry.ifOutOctets.4 = 1256486519

So now I have two OIDs to monitor and they are (in full, this time):

   1.3.6.1.2.1.2.2.1.10

and

   1.3.6.1.2.1.2.2.1.16

both with an interface number of 4.

Don't get fooled, this wasn't my first try. It took some time for me too to understand what all these numbers mean. It does help a lot when they get translated into descriptive text... At least, when people are talking about MIBs and OIDs you know what it's all about. Do not forget the interface number (0 if it is not interface dependent) and try snmpwalk if you don't get an answer from snmpget.

If you understand the above section and get numbers from your device, continue on with this tutorial. If not, then go back and re-read this part.

A Real World Example

Let the fun begin. First, create a new database. It contains data from two counters, called input and output. The data is put into archives that average it. They take 1, 6, 24 or 288 samples at a time. They also go into archives that keep the maximum numbers. This will be explained later on. The time in-between samples is 300 seconds, a good starting point, which is the same as five minutes.

 1 sample "averaged" stays 1 period of 5 minutes
 6 samples averaged become one average on 30 minutes
 24 samples averaged become one average on 2 hours
 288 samples averaged become one average on 1 day

Lets try to be compatible with MRTG which stores about the following amount of data:

 600 5-minute samples:    2   days and 2 hours
 600 30-minute samples:  12.5 days
 600 2-hour samples:     50   days
 732 1-day samples:     732   days

These ranges are appended, so the total amount of data stored in the database is approximately 797 days. RRDtool stores the data differently, it doesn't start the "weekly" archive where the "daily" archive stopped. For both archives the most recent data will be near "now" and therefore we will need to keep more data than MRTG does!

We will need:

 600 samples of 5 minutes  (2 days and 2 hours)
 700 samples of 30 minutes (2 days and 2 hours, plus 12.5 days)
 775 samples of 2 hours    (above + 50 days)
 797 samples of 1 day      (above + 732 days, rounded up to 797)
   rrdtool create myrouter.rrd         \
            DS:input:COUNTER:600:U:U   \
            DS:output:COUNTER:600:U:U  \
            RRA:AVERAGE:0.5:1:600      \
            RRA:AVERAGE:0.5:6:700      \
            RRA:AVERAGE:0.5:24:775     \
            RRA:AVERAGE:0.5:288:797    \
            RRA:MAX:0.5:1:600          \
            RRA:MAX:0.5:6:700          \
            RRA:MAX:0.5:24:775         \
            RRA:MAX:0.5:288:797

Next thing to do is to collect data and store it. Here is an example. It is written partially in pseudo code, you will have to find out what to do exactly on your OS to make it work.

   while not the end of the universe
   do
      get result of
         snmpget router community 2.2.1.10.4
      into variable $in
      get result of
         snmpget router community 2.2.1.16.4
      into variable $out
      rrdtool update myrouter.rrd N:$in:$out
      wait for 5 minutes
   done

Then, after collecting data for a day, try to create an image using:

   rrdtool graph myrouter-day.png --start -86400 \
            DEF:inoctets=myrouter.rrd:input:AVERAGE \
            DEF:outoctets=myrouter.rrd:output:AVERAGE \
            AREA:inoctets#00FF00:"In traffic" \
            LINE1:outoctets#0000FF:"Out traffic"

This should produce a picture with one day worth of traffic. One day is 24 hours of 60 minutes of 60 seconds: 24*60*60=86400, we start at now minus 86400 seconds. We define (with DEFs) inoctets and outoctets as the average values from the database myrouter.rrd and draw an area for the "in" traffic and a line for the "out" traffic.

View the image and keep logging data for a few more days. If you like, you could try the examples from the test database and see if you can get various options and calculations to work.

Suggestion: Display in bytes per second and in bits per second. Make the Ethernet graphics go red if they are over four megabits per second.

Consolidation Functions

A few paragraphs back I mentioned the possibility of keeping the maximum values instead of the average values. Let's go into this a bit more.

Recall all the stuff about the speed of the car. Suppose we drove at 144 km/h during 5 minutes and then were stopped by the police for 25 minutes. At the end of the lecture we would take our laptop and create and view the image taken from the database. If we look at the second RRA we did create, we would have the average from 6 samples. The samples measured would be 144+0+0+0+0+0=144, divided by 30 minutes, corrected for the error by 1000, translated into km/h, with a result of 24 km/h. I would still get a ticket but not for speeding anymore :)

Obviously, in this case we shouldn't look at the averages. In some cases they are handy. If you want to know how many km you had traveled, the averaged picture would be the right one to look at. On the other hand, for the speed that we traveled at, the maximum numbers seen is much more interesting. Later we will see more types.

It is the same for data. If you want to know the amount, look at the averages. If you want to know the rate, look at the maximum. Over time, they will grow apart more and more. In the last database we have created, there are two archives that keep data per day. The archive that keeps averages will show low numbers, the archive that shows maxima will have higher numbers.

For my car this would translate in averages per day of 96/24=4 km/h (as I travel about 94 kilometers on a day) during working days, and maxima of 120 km/h (my top speed that I reach every day).

Big difference. Do not look at the second graph to estimate the distances that I travel and do not look at the first graph to estimate my speed. This will work if the samples are close together, as they are in five minutes, but not if you average.

On some days, I go for a long ride. If I go across Europe and travel for 12 hours, the first graph will rise to about 60 km/h. The second one will show 180 km/h. This means that I traveled a distance of 60 km/h times 24 h = 1440 km. I did this with a higher speed and a maximum around 180 km/h. However, it probably doesn't mean that I traveled for 8 hours at a constant speed of 180 km/h!

This is a real example: go with the flow through Germany (fast!) and stop a few times for gas and coffee. Drive slowly through Austria and the Netherlands. Be careful in the mountains and villages. If you would look at the graphs created from the five-minute averages you would get a totally different picture. You would see the same values on the average and maximum graphs (provided I measured every 300 seconds). You would be able to see when I stopped, when I was in top gear, when I drove over fast highways etc. The granularity of the data is much higher, so you can see more. However, this takes 12 samples per hour, or 288 values per day, so it would be a lot of data over a longer period of time. Therefore we average it, eventually to one value per day. From this one value, we cannot see much detail, of course.

Make sure you understand the last few paragraphs. There is no value in only a line and a few axis, you need to know what they mean and interpret the data in an appropriate way. This is true for all data.

The biggest mistake you can make is to use the collected data for something that it is not suitable for. You would be better off if you didn't have the graph at all.

Let's review what you now should know

You know how to create a database and can put data in it. You can get the numbers out again by creating an image, do math on the data from the database and view the result instead of the raw data. You know about the difference between averages and maximum, and when to use which (or at least you should have an idea).

RRDtool can do more than what we have learned up to now. Before you continue with the rest of this doc, I recommend that you reread from the start and try some modifications on the examples. Make sure you fully understand everything. It will be worth the effort and helps you not only with the rest of this tutorial, but also in your day to day monitoring long after you read this introduction.

Data Source Types

All right, you feel like continuing. Welcome back and get ready for an increased speed in the examples and explanations.

You know that in order to view a counter over time, you have to take two numbers and divide the difference of them between the time lapsed. This makes sense for the examples I gave you but there are other possibilities. For instance, I'm able to retrieve the temperature from my router in three places namely the inlet, the so called hot-spot and the exhaust. These values are not counters. If I take the difference of the two samples and divide that by 300 seconds I would be asking for the temperature change per second. Hopefully this is zero! If not, the computer room is probably on fire :)

So, what can we do? We can tell RRDtool to store the values we measure directly as they are (this is not entirely true but close enough). The graphs we make will look much better, they will show a rather constant value. I know when the router is busy (it works -> it uses more electricity -> it generates more heat -> the temperature rises). I know when the doors are left open (the room is air conditioned) -> the warm air from the rest of the building flows into the computer room -> the inlet temperature rises). Etc. The data type we use when creating the database before was counter, we now have a different data type and thus a different name for it. It is called GAUGE. There are more such data types:

 - COUNTER   we already know this one
 - GAUGE     we just learned this one
 - DERIVE
 - ABSOLUTE

The two additional types are DERIVE and ABSOLUTE. Absolute can be used like counter with one difference: RRDtool assumes the counter is reset when it's read. That is: its delta is known without calculation by RRDtool whereas RRDtool needs to calculate it for the counter type. Example: our first example (12345, 12357, 12363, 12363) would read: unknown, 12, 6, 0. The rest of the calculations stay the same. The other one, derive, is like counter. Unlike counter, it can also decrease so it can have a negative delta. Again, the rest of the calculations stay the same.

Let's try them all:

   rrdtool create all.rrd --start 978300900 \
            DS:a:COUNTER:600:U:U \
            DS:b:GAUGE:600:U:U \
            DS:c:DERIVE:600:U:U \
            DS:d:ABSOLUTE:600:U:U \
            RRA:AVERAGE:0.5:1:10
   rrdtool update all.rrd \
            978301200:300:1:600:300    \
            978301500:600:3:1200:600   \
            978301800:900:5:1800:900   \
            978302100:1200:3:2400:1200 \
            978302400:1500:1:2400:1500 \
            978302700:1800:2:1800:1800 \
            978303000:2100:4:0:2100    \
            978303300:2400:6:600:2400  \
            978303600:2700:4:600:2700  \
            978303900:3000:2:1200:3000
   rrdtool graph all1.png -s 978300600 -e 978304200 -h 400 \
            DEF:linea=all.rrd:a:AVERAGE LINE3:linea#FF0000:"Line A" \
            DEF:lineb=all.rrd:b:AVERAGE LINE3:lineb#00FF00:"Line B" \
            DEF:linec=all.rrd:c:AVERAGE LINE3:linec#0000FF:"Line C" \
            DEF:lined=all.rrd:d:AVERAGE LINE3:lined#000000:"Line D"

RRDtool under the Microscope

  • Line A is a COUNTER type, so it should continuously increment and RRDtool must calculate the differences. Also, RRDtool needs to divide the difference by the amount of time lapsed. This should end up as a straight line at 1 (the deltas are 300, the time is 300).

  • Line B is of type GAUGE. These are "real" values so they should match what we put in: a sort of a wave.

  • Line C is of type DERIVE. It should be a counter that can decrease. It does so between 2400 and 0, with 1800 in-between.

  • Line D is of type ABSOLUTE. This is like counter but it works on values without calculating the difference. The numbers are the same and as you can see (hopefully) this has a different result.

This translates in the following values, starting at 23:10 and ending at 00:10 the next day (where "u" means unknown/unplotted):

 - Line A:  u  u  1  1  1  1  1  1  1  1  1  u
 - Line B:  u  1  3  5  3  1  2  4  6  4  2  u
 - Line C:  u  u  2  2  2  0 -2 -6  2  0  2  u
 - Line D:  u  1  2  3  4  5  6  7  8  9 10  u

If your PNG shows all this, you know you have entered the data correctly, the RRDtool executable is working properly, your viewer doesn't fool you, and you successfully entered the year 2000 :)

You could try the same example four times, each time with only one of the lines.

Let's go over the data again:

  • Line A: 300,600,900 and so on. The counter delta is a constant 300 and so is the time delta. A number divided by itself is always 1 (except when dividing by zero which is undefined/illegal).

    Why is it that the first point is unknown? We do know what we put into the database, right? True, But we didn't have a value to calculate the delta from, so we don't know where we started. It would be wrong to assume we started at zero so we don't!

  • Line B: There is nothing to calculate. The numbers are as they are.

  • Line C: Again, the start-out value is unknown. The same story is holds as for line A. In this case the deltas are not constant, therefore the line is not either. If we would put the same numbers in the database as we did for line A, we would have gotten the same line. Unlike type counter, this type can decrease and I hope to show you later on why this makes a difference.

  • Line D: Here the device calculates the deltas. Therefore we DO know the first delta and it is plotted. We had the same input as with line A, but the meaning of this input is different and thus the line is different. In this case the deltas increase each time with 300. The time delta stays at a constant 300 and therefore the division of the two gives increasing values.

Counter Wraps

There are a few more basics to show. Some important options are still to be covered and we haven't look at counter wraps yet. First the counter wrap: In our car we notice that the counter shows 999987. We travel 20 km and the counter should go to 1000007. Unfortunately, there are only six digits on our counter so it really shows 000007. If we would plot that on a type DERIVE, it would mean that the counter was set back 999980 km. It wasn't, and there has to be some protection for this. This protection is only available for type COUNTER which should be used for this kind of counter anyways. How does it work? Type counter should never decrease and therefore RRDtool must assume it wrapped if it does decrease! If the delta is negative, this can be compensated for by adding the maximum value of the counter + 1. For our car this would be:

 Delta = 7 - 999987 = -999980    (instead of 1000007-999987=20)
 Real delta = -999980 + 999999 + 1 = 20

At the time of writing this document, RRDtool knows of counters that are either 32 bits or 64 bits of size. These counters can handle the following different values:

 - 32 bits: 0 ..           4294967295
 - 64 bits: 0 .. 18446744073709551615

If these numbers look strange to you, you can view them in their hexadecimal form:

 - 32 bits: 0 ..         FFFFFFFF
 - 64 bits: 0 .. FFFFFFFFFFFFFFFF

RRDtool handles both counters the same. If an overflow occurs and the delta would be negative, RRDtool first adds the maximum of a small counter + 1 to the delta. If the delta is still negative, it had to be the large counter that wrapped. Add the maximum possible value of the large counter + 1 and subtract the erroneously added small value.

There is a risk in this: suppose the large counter wrapped while adding a huge delta, it could happen, theoretically, that adding the smaller value would make the delta positive. In this unlikely case the results would not be correct. The increase should be nearly as high as the maximum counter value for that to happen, so chances are you would have several other problems as well and this particular problem would not even be worth thinking about. Even though, I did include an example, so you can judge for yourself.

The next section gives you some numerical examples for counter-wraps. Try to do the calculations yourself or just believe me if your calculator can't handle the numbers :)

Correction numbers:

 - 32 bits: (4294967295 + 1) =                                4294967296
 - 64 bits: (18446744073709551615 + 1)
                                    - correction1 = 18446744069414584320
 Before:        4294967200
 Increase:                100
 Should become: 4294967300
 But really is:             4
 Delta:        -4294967196
 Correction1:  -4294967196 + 4294967296 = 100
 Before:        18446744073709551000
 Increase:                             800
 Should become: 18446744073709551800
 But really is:                        184
 Delta:        -18446744073709550816
 Correction1:  -18446744073709550816
                                + 4294967296 = -18446744069414583520
 Correction2:  -18446744069414583520
                   + 18446744069414584320 = 800
 Before:        18446744073709551615 ( maximum value )
 Increase:      18446744069414584320 ( absurd increase, minimum for
 Should become: 36893488143124135935             this example to work )
 But really is: 18446744069414584319
 Delta:                     -4294967296
 Correction1:  -4294967296 + 4294967296 = 0
 (not negative -> no correction2)
 Before:        18446744073709551615 ( maximum value )
 Increase:      18446744069414584319 ( one less increase )
 Should become: 36893488143124135934
 But really is: 18446744069414584318
 Delta:                     -4294967297
 Correction1:  -4294967297 + 4294967296 = -1
 Correction2:  -1 + 18446744069414584320 = 18446744069414584319

As you can see from the last two examples, you need strange numbers for RRDtool to fail (provided it's bug free of course), so this should not happen. However, SNMP or whatever method you choose to collect the data, might also report wrong numbers occasionally. We can't prevent all errors, but there are some things we can do. The RRDtool "create" command takes two special parameters for this. They define the minimum and maximum allowed values. Until now, we used "U", meaning "unknown". If you provide values for one or both of them and if RRDtool receives data points that are outside these limits, it will ignore those values. For a thermometer in degrees Celsius, the absolute minimum is just under -273. For my router, I can assume this minimum is much higher so I would set it to 10, where as the maximum temperature I would set to 80. Any higher and the device would be out of order.

For the speed of my car, I would never expect negative numbers and also I would not expect a speed higher than 230. Anything else, and there must have been an error. Remember: the opposite is not true, if the numbers pass this check, it doesn't mean that they are correct. Always judge the graph with a healthy dose of suspicion if it seems weird to you.

Data Resampling

One important feature of RRDtool has not been explained yet: it is virtually impossible to collect data and feed it into RRDtool on exact intervals. RRDtool therefore interpolates the data, so they are stored on exact intervals. If you do not know what this means or how it works, then here's the help you seek:

Suppose a counter increases by exactly one for every second. You want to measure it in 300 seconds intervals. You should retrieve values that are exactly 300 apart. However, due to various circumstances you are a few seconds late and the interval is 303. The delta will also be 303 in that case. Obviously, RRDtool should not put 303 in the database and make you believe that the counter increased by 303 in 300 seconds. This is where RRDtool interpolates: it alters the 303 value as if it would have been stored earlier and it will be 300 in 300 seconds. Next time you are at exactly the right time. This means that the current interval is 297 seconds and also the counter increased by 297. Again, RRDtool interpolates and stores 300 as it should be.

      in the RRD                 in reality
 time+000:   0 delta="U"   time+000:    0 delta="U"
 time+300: 300 delta=300   time+300:  300 delta=300
 time+600: 600 delta=300   time+603:  603 delta=303
 time+900: 900 delta=300   time+900:  900 delta=297

Let's create two identical databases. I've chosen the time range 920805000 to 920805900 as this goes very well with the example numbers.

   rrdtool create seconds1.rrd   \
      --start 920804700          \
      DS:seconds:COUNTER:600:U:U \
      RRA:AVERAGE:0.5:1:24

Make a copy

   for Unix: cp seconds1.rrd seconds2.rrd
   for Dos:  copy seconds1.rrd seconds2.rrd
   for vms:  how would I know :)

Put in some data

   rrdtool update seconds1.rrd \
      920805000:000 920805300:300 920805600:600 920805900:900
   rrdtool update seconds2.rrd \
      920805000:000 920805300:300 920805603:603 920805900:900

Create output

   rrdtool graph seconds1.png                       \
      --start 920804700 --end 920806200             \
      --height 200                                  \
      --upper-limit 1.05 --lower-limit 0.95 --rigid \
      DEF:seconds=seconds1.rrd:seconds:AVERAGE      \
      CDEF:unknown=seconds,UN                       \
      LINE2:seconds#0000FF                          \
      AREA:unknown#FF0000
   rrdtool graph seconds2.png                       \
      --start 920804700 --end 920806200             \
      --height 200                                  \
      --upper-limit 1.05 --lower-limit 0.95 --rigid \
      DEF:seconds=seconds2.rrd:seconds:AVERAGE      \
      CDEF:unknown=seconds,UN                       \
      LINE2:seconds#0000FF                          \
      AREA:unknown#FF0000

View both images together (add them to your index.html file) and compare. Both graphs should show the same, despite the input being different.


WRAPUP

It's time now to wrap up this tutorial. We covered all the basics for you to be able to work with RRDtool and to read the additional documentation available. There is plenty more to discover about RRDtool and you will find more and more uses for this package. You can easily create graphs using just the examples provided and using only RRDtool. You can also use one of the front ends to RRDtool that are available.


MAILINGLIST

Remember to subscribe to the RRDtool mailing list. Even if you are not answering to mails that come by, it helps both you and the rest of the users. A lot of the stuff that I know about MRTG (and therefore about RRDtool) I've learned while just reading the list without posting to it. I did not need to ask the basic questions as they are answered in the FAQ (read it!) and in various mails by other users. With thousands of users all over the world, there will always be people who ask questions that you can answer because you read this and other documentation and they didn't.


SEE ALSO

The RRDtool manpages


AUTHOR

I hope you enjoyed the examples and their descriptions. If you do, help other people by pointing them to this document when they are asking basic questions. They will not only get their answers, but at the same time learn a whole lot more.

Alex van den Bogaerdt <alex@vandenbogaerdt.nl>

rrdtool-1.4.7/doc/rrdresize.html0000644000175300017510000000540411707501545013611 00000000000000 rrdresize

NAME

rrdresize - alters the size of an RRA and creates a new .rrd file


SYNOPSIS

rrdtool resize filename rra-num GROW|SHRINK rows


DESCRIPTION

The resize function is used to modify the number of rows in an RRA.

filename

the name of the RRD you want to alter.

rra-num

the RRA you want to alter. You can find the number using rrdtool info.

GROW

used if you want to add extra rows to an RRA. The extra rows will be inserted as the rows that are oldest.

SHRINK

used if you want to remove rows from an RRA. The rows that will be removed are the oldest rows.

rows

the number of rows you want to add or remove.


NOTES

The new .rrd file, with the modified RRAs, is written to the file resize.rrd in the current directory. The original .rrd file is not modified.

It is possible to abuse this tool and get strange results by first removing some rows and then reinserting the same amount (effectively clearing them to be Unknown). You may thus end up with unknown data in one RRA while at the same timestamp this data is available in another RRA.


AUTHOR

Alex van den Bogaerdt <alex@vandenbogaerdt.nl>

rrdtool-1.4.7/doc/rrdgraph_data.html0000644000175300017510000002005111707501545014375 00000000000000 rrdgraph_data

NAME

rrdgraph_data - preparing data for graphing in rrdtool graph


SYNOPSIS

DEF:<vname>=<rrdfile>:<ds-name>:<CF>[:step=<step>][:start=<time>][:end=<time>][:reduce=<CF>]

VDEF:vname=RPN expression

CDEF:vname=RPN expression


DESCRIPTION

These three instructions extract data values out of the RRD files, optionally altering them (think, for example, of a bytes to bits conversion). If so desired, you can also define variables containing useful information such as maximum, minimum etcetera. Two of the instructions use a language called RPN which is described in its own manual page.

Variable names (vname) must be made up strings of the following characters A-Z, a-z, 0-9, -,_ and a maximum length of 255 characters.

When picking variable names, make sure you do not choose a name that is already taken by an RPN operator. A safe bet it to use lowercase or mixed case names for variables since operators will always be in uppercase.


DEF

DEF:<vname>=<rrdfile>:<ds-name>:<CF>[:step=<step>][:start=<time>][:end=<time>][:reduce=<CF>]

This command fetches data from an RRD file. The virtual name vname can then be used throughout the rest of the script. By default, an RRA which contains the correct consolidated data at an appropriate resolution will be chosen. The resolution can be overridden with the --step option. The resolution can again be overridden by specifying the step size. The time span of this data is the same as for the graph by default, you can override this by specifying start and end. Remember to escape colons in the time specification!

If the resolution of the data is higher than the resolution of the graph, the data will be further consolidated. This may result in a graph that spans slightly more time than requested. Ideally each point in the graph should correspond with one CDP from an RRA. For instance, if your RRD has an RRA with a resolution of 1800 seconds per CDP, you should create an image with width 400 and time span 400*1800 seconds (use appropriate start and end times, such as --start end-8days8hours).

If consolidation needs to be done, the CF of the RRA specified in the DEF itself will be used to reduce the data density. This behavior can be changed using :reduce=<CF>. This optional parameter specifies the CF to use during the data reduction phase.

Example:

        DEF:ds0=router.rrd:ds0:AVERAGE
        DEF:ds0weekly=router.rrd:ds0:AVERAGE:step=7200
        DEF:ds0weekly=router.rrd:ds0:AVERAGE:start=end-1h
        DEF:ds0weekly=router.rrd:ds0:AVERAGE:start=11\:00:end=start+1h


VDEF

VDEF:vname=RPN expression

This command returns a value and/or a time according to the RPN statements used. The resulting vname will, depending on the functions used, have a value and a time component. When you use this vname in another RPN expression, you are effectively inserting its value just as if you had put a number at that place. The variable can also be used in the various graph and print elements.

Example: VDEF:avg=mydata,AVERAGE

Note that currently only aggregation functions work in VDEF rpn expressions. Patches to change this are welcome.


CDEF

CDEF:vname=RPN expression

This command creates a new set of data points (in memory only, not in the RRD file) out of one or more other data series. The RPN instructions are used to evaluate a mathematical function on each data point. The resulting vname can then be used further on in the script, just as if it were generated by a DEF instruction.

Example: CDEF:mydatabits=mydata,8,*


About CDEF versus VDEF

At some point in processing, RRDtool has gathered an array of rates ready to display.

CDEF works on such an array. For example, CDEF:new=ds0,8,* would multiply each of the array members by eight (probably transforming bytes into bits). The result is an array containing the new values.

VDEF also works on such an array but in a different way. For example, VDEF:max=ds0,MAXIMUM would scan each of the array members and store the maximum value.

When do you use VDEF versus CDEF?

Use CDEF to transform your data prior to graphing. In the above example, we'd use a CDEF to transform bytes to bits before graphing the bits.

You use a VDEF if you want max(1,5,3,2,4) to return five which would be displayed in the graph's legend (to answer, what was the maximum value during the graph period).

If you want to apply 'complex' operations to the result of a VDEF you have to use a CDEF again since VDEFs only look like RPN expressions, they aren't really.


SEE ALSO

the rrdgraph manpage gives an overview of how rrdtool graph works. the rrdgraph_data manpage describes DEF,CDEF and VDEF in detail. the rrdgraph_rpn manpage describes the RPN language used in the ?DEF statements. the rrdgraph_graph manpage page describes all of the graph and print functions.

Make sure to read the rrdgraph_examples manpage for tips&tricks.


AUTHOR

Program by Tobias Oetiker <tobi@oetiker.ch>

This manual page by Alex van den Bogaerdt <alex@vandenbogaerdt.nl> with corrections and/or additions by several people

rrdtool-1.4.7/doc/rrdcreate.html0000644000175300017510000007716311707501545013566 00000000000000 rrdcreate

NAME

rrdcreate - Set up a new Round Robin Database


SYNOPSIS

rrdtool create filename [--start|-b start time] [--step|-s step] [--no-overwrite] [DS:ds-name:DST:dst arguments] [RRA:CF:cf arguments]


DESCRIPTION

The create function of RRDtool lets you set up new Round Robin Database (RRD) files. The file is created at its final, full size and filled with *UNKNOWN* data.

filename

The name of the RRD you want to create. RRD files should end with the extension .rrd. However, RRDtool will accept any filename.

--start|-b start time (default: now - 10s)

Specifies the time in seconds since 1970-01-01 UTC when the first value should be added to the RRD. RRDtool will not accept any data timed before or at the time specified.

See also AT-STYLE TIME SPECIFICATION section in the rrdfetch documentation for other ways to specify time.

--step|-s step (default: 300 seconds)

Specifies the base interval in seconds with which data will be fed into the RRD.

--no-overwrite

Do not clobber an existing file of the same name.

DS:ds-name:DST:dst arguments

A single RRD can accept input from several data sources (DS), for example incoming and outgoing traffic on a specific communication line. With the DS configuration option you must define some basic properties of each data source you want to store in the RRD.

ds-name is the name you will use to reference this particular data source from an RRD. A ds-name must be 1 to 19 characters long in the characters [a-zA-Z0-9_].

DST defines the Data Source Type. The remaining arguments of a data source entry depend on the data source type. For GAUGE, COUNTER, DERIVE, and ABSOLUTE the format for a data source entry is:

DS:ds-name:GAUGE | COUNTER | DERIVE | ABSOLUTE:heartbeat:min:max

For COMPUTE data sources, the format is:

DS:ds-name:COMPUTE:rpn-expression

In order to decide which data source type to use, review the definitions that follow. Also consult the section on "HOW TO MEASURE" for further insight.

GAUGE

is for things like temperatures or number of people in a room or the value of a RedHat share.

COUNTER

is for continuous incrementing counters like the ifInOctets counter in a router. The COUNTER 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.

DERIVE

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.

NOTE on COUNTER vs DERIVE

by Don Baarda <don.baarda@baesystems.com>

If you cannot tolerate ever mistaking the occasional counter reset for a legitimate counter wrap, and would prefer "Unknowns" for all legitimate counter wraps and resets, always use DERIVE with min=0. Otherwise, using COUNTER with a suitable max will return correct values for all legitimate counter wraps, mark some counter resets as "Unknown", but can mistake some counter resets for a legitimate counter wrap.

For a 5 minute step and 32-bit counter, the probability of mistaking a counter reset for a legitimate wrap is arguably about 0.8% per 1Mbps of maximum bandwidth. Note that this equates to 80% for 100Mbps interfaces, so for high bandwidth interfaces and a 32bit counter, DERIVE with min=0 is probably preferable. If you are using a 64bit counter, just about any max setting will eliminate the possibility of mistaking a reset for a counter wrap.

ABSOLUTE

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 maximum time available before the next overflow. Another usage is for things you count like number of messages since the last update.

COMPUTE

is for storing the result of a formula applied to other data sources in the RRD. This data source is not supplied a value on update, but rather its Primary Data Points (PDPs) are computed from the PDPs of the data sources according to the rpn-expression that defines the formula. Consolidation functions are then applied normally to the PDPs of the COMPUTE data source (that is the rpn-expression is only applied to generate PDPs). In database software, such data sets are referred to as "virtual" or "computed" columns.

heartbeat 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 *UNKNOWN*.

min and max define the expected range values for data supplied by a data source. If min and/or max are specified any value outside the defined range will be regarded as *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-COUNTER type DS this would be the maximum and minimum data-rate expected from the device.

If information on minimal/maximal expected values is available, always set the min and/or max properties. This will help RRDtool in doing a simple sanity check on the data supplied when running update.

rpn-expression defines the formula used to compute the PDPs of a COMPUTE data source from other data sources in the same <RRD>. It is similar to defining a CDEF argument for the graph command. Please refer to that manual page for a list and description of RPN operations supported. For COMPUTE data sources, the following RPN operations are not supported: COUNT, PREV, TIME, and LTIME. In addition, in defining the RPN expression, the COMPUTE data source may only refer to the names of data source listed previously in the create command. This is similar to the restriction that CDEFs must refer only to DEFs and CDEFs previously defined in the same graph command.

RRA:CF:cf arguments

The purpose of an RRD is to store data in the round robin archives (RRA). An archive consists of a number of data values or statistics for each of the defined data-sources (DS) and is defined with an RRA line.

When data is entered into an RRD, it is first fit into time slots of the length defined with the -s option, thus becoming a primary data point.

The data is also processed with the consolidation function (CF) of the archive. There are several consolidation functions that consolidate primary data points via an aggregate function: AVERAGE, MIN, MAX, LAST.

AVERAGE

the average of the data points is stored.

MIN

the smallest of the data points is stored.

MAX

the largest of the data points is stored.

LAST

the last data points is used.

Note that data aggregation inevitably leads to loss of precision and information. The trick is to pick the aggregate function such that the interesting properties of your data is kept across the aggregation process.

The format of RRA line for these consolidation functions is:

RRA:AVERAGE | MIN | MAX | LAST:xff:steps:rows

xff The xfiles factor defines what part of a consolidation interval may be made up from *UNKNOWN* data while the consolidated value is still regarded as known. It is given as the ratio of allowed *UNKNOWN* PDPs to the number of PDPs in the interval. Thus, it ranges from 0 to 1 (exclusive).

steps defines how many of these primary data points are used to build a consolidated data point which then goes into the archive.

rows defines how many generations of data values are kept in an RRA. Obviously, this has to be greater than zero.


Aberrant Behavior Detection with Holt-Winters Forecasting

In addition to the aggregate functions, there are a set of specialized functions that enable RRDtool to provide data smoothing (via the Holt-Winters forecasting algorithm), confidence bands, and the flagging aberrant behavior in the data source time series:

  • RRA:HWPREDICT:rows:alpha:beta:seasonal period[:rra-num]

  • RRA:MHWPREDICT:rows:alpha:beta:seasonal period[:rra-num]

  • RRA:SEASONAL:seasonal period:gamma:rra-num[:smoothing-window=fraction]

  • RRA:DEVSEASONAL:seasonal period:gamma:rra-num[:smoothing-window=fraction]

  • RRA:DEVPREDICT:rows:rra-num

  • RRA:FAILURES:rows:threshold:window length:rra-num

These RRAs differ from the true consolidation functions in several ways. First, each of the RRAs is updated once for every primary data point. Second, these RRAs are interdependent. To generate real-time confidence bounds, a matched set of SEASONAL, DEVSEASONAL, DEVPREDICT, and either HWPREDICT or MHWPREDICT must exist. Generating smoothed values of the primary data points requires a SEASONAL RRA and either an HWPREDICT or MHWPREDICT RRA. Aberrant behavior detection requires FAILURES, DEVSEASONAL, SEASONAL, and either HWPREDICT or MHWPREDICT.

The predicted, or smoothed, values are stored in the HWPREDICT or MHWPREDICT RRA. HWPREDICT and MHWPREDICT are actually two variations on the Holt-Winters method. They are interchangeable. Both attempt to decompose data into three components: a baseline, a trend, and a seasonal coefficient. HWPREDICT adds its seasonal coefficient to the baseline to form a prediction, whereas MHWPREDICT multiplies its seasonal coefficient by the baseline to form a prediction. The difference is noticeable when the baseline changes significantly in the course of a season; HWPREDICT will predict the seasonality to stay constant as the baseline changes, but MHWPREDICT will predict the seasonality to grow or shrink in proportion to the baseline. The proper choice of method depends on the thing being modeled. For simplicity, the rest of this discussion will refer to HWPREDICT, but MHWPREDICT may be substituted in its place.

The predicted deviations are stored in DEVPREDICT (think a standard deviation which can be scaled to yield a confidence band). The FAILURES RRA stores binary indicators. A 1 marks the indexed observation as failure; that is, the number of confidence bounds violations in the preceding window of observations met or exceeded a specified threshold. An example of using these RRAs to graph confidence bounds and failures appears in the rrdgraph manpage.

The SEASONAL and DEVSEASONAL RRAs store the seasonal coefficients for the Holt-Winters forecasting algorithm and the seasonal deviations, respectively. There is one entry per observation time point in the seasonal cycle. For example, if primary data points are generated every five minutes and the seasonal cycle is 1 day, both SEASONAL and DEVSEASONAL will have 288 rows.

In order to simplify the creation for the novice user, in addition to supporting explicit creation of the HWPREDICT, SEASONAL, DEVPREDICT, DEVSEASONAL, and FAILURES RRAs, the RRDtool create command supports implicit creation of the other four when HWPREDICT is specified alone and the final argument rra-num is omitted.

rows specifies the length of the RRA prior to wrap around. Remember that there is a one-to-one correspondence between primary data points and entries in these RRAs. For the HWPREDICT CF, rows should be larger than the seasonal period. If the DEVPREDICT RRA is implicitly created, the default number of rows is the same as the HWPREDICT rows argument. If the FAILURES RRA is implicitly created, rows will be set to the seasonal period argument of the HWPREDICT RRA. Of course, the RRDtool resize command is available if these defaults are not sufficient and the creator wishes to avoid explicit creations of the other specialized function RRAs.

seasonal period specifies the number of primary data points in a seasonal cycle. If SEASONAL and DEVSEASONAL are implicitly created, this argument for those RRAs is set automatically to the value specified by HWPREDICT. If they are explicitly created, the creator should verify that all three seasonal period arguments agree.

alpha is the adaption parameter of the intercept (or baseline) coefficient in the Holt-Winters forecasting algorithm. See the rrdtool manpage for a description of this algorithm. alpha must lie between 0 and 1. A value closer to 1 means that more recent observations carry greater weight in predicting the baseline component of the forecast. A value closer to 0 means that past history carries greater weight in predicting the baseline component.

beta is the adaption parameter of the slope (or linear trend) coefficient in the Holt-Winters forecasting algorithm. beta must lie between 0 and 1 and plays the same role as alpha with respect to the predicted linear trend.

gamma is the adaption parameter of the seasonal coefficients in the Holt-Winters forecasting algorithm (HWPREDICT) or the adaption parameter in the exponential smoothing update of the seasonal deviations. It must lie between 0 and 1. If the SEASONAL and DEVSEASONAL RRAs are created implicitly, they will both have the same value for gamma: the value specified for the HWPREDICT alpha argument. Note that because there is one seasonal coefficient (or deviation) for each time point during the seasonal cycle, the adaptation rate is much slower than the baseline. Each seasonal coefficient is only updated (or adapts) when the observed value occurs at the offset in the seasonal cycle corresponding to that coefficient.

If SEASONAL and DEVSEASONAL RRAs are created explicitly, gamma need not be the same for both. Note that gamma can also be changed via the RRDtool tune command.

smoothing-window specifies the fraction of a season that should be averaged around each point. By default, the value of smoothing-window is 0.05, which means each value in SEASONAL and DEVSEASONAL will be occasionally replaced by averaging it with its (seasonal period*0.05) nearest neighbors. Setting smoothing-window to zero will disable the running-average smoother altogether.

rra-num provides the links between related RRAs. If HWPREDICT is specified alone and the other RRAs are created implicitly, then there is no need to worry about this argument. If RRAs are created explicitly, then carefully pay attention to this argument. For each RRA which includes this argument, there is a dependency between that RRA and another RRA. The rra-num argument is the 1-based index in the order of RRA creation (that is, the order they appear in the create command). The dependent RRA for each RRA requiring the rra-num argument is listed here:

  • HWPREDICT rra-num is the index of the SEASONAL RRA.

  • SEASONAL rra-num is the index of the HWPREDICT RRA.

  • DEVPREDICT rra-num is the index of the DEVSEASONAL RRA.

  • DEVSEASONAL rra-num is the index of the HWPREDICT RRA.

  • FAILURES rra-num is the index of the DEVSEASONAL RRA.

threshold is the minimum number of violations (observed values outside the confidence bounds) within a window that constitutes a failure. If the FAILURES RRA is implicitly created, the default value is 7.

window length is the number of time points in the window. Specify an integer greater than or equal to the threshold and less than or equal to 28. The time interval this window represents depends on the interval between primary data points. If the FAILURES RRA is implicitly created, the default value is 9.


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:

RRDtool gets fed samples/updates at arbitrary times. From these it builds Primary Data Points (PDPs) on every "step" interval. The PDPs are then accumulated into the RRAs.

The "heartbeat" defines the maximum acceptable interval between samples/updates. 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 a sample that was explicitly marked as unknown.

The known rates during a PDP's "step" interval are used to calculate an average rate for that PDP. If the total "unknown" time accounts for more than half the "step", the entire PDP is marked as "unknown". This means that a mixture of known and "unknown" sample times in a single PDP "step" may or may not add up to enough "known" time to warrant a known PDP.

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 5 minutes 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. -- Don Baarda <don.baarda@baesystems.com>

       time|
       axis|
 begin__|00|
        |01|
       u|02|----* sample1, restart "hb"-timer
       u|03|   /
       u|04|  /
       u|05| /
       u|06|/     "hbt" expired
       u|07|
        |08|----* sample2, restart "hb" 
        |09|   / 
        |10|  /
       u|11|----* sample3, restart "hb"
       u|12|   /
       u|13|  /
 step1_u|14| /
       u|15|/     "swt" expired
       u|16|
        |17|----* sample4, restart "hb", create "pdp" for step1 = 
        |18|   /  = unknown due to 10 "u" labled secs > 0.5 * step
        |19|  /
        |20| /
        |21|----* sample5, restart "hb"
        |22|   /
        |23|  /
        |24|----* sample6, restart "hb"
        |25|   /
        |26|  /
        |27|----* sample7, restart "hb"
 step2__|28|   /
        |22|  /
        |23|----* sample8, restart "hb", create "pdp" for step1, create "cdp" 
        |24|   /
        |25|  /

graphics by vladimir.lavrov@desy.de.


HOW TO MEASURE

Here are a few hints on how to measure:

Temperature

Usually you have some type of meter you can read to get the temperature. The temperature is not really connected with a time. The only connection is that the temperature reading happened at a certain time. You can use the GAUGE data source type for this. RRDtool will then record your reading together with the time.

Mail Messages

Assume you have a method to count the number of messages transported by your mail server in a certain amount of time, giving you data like '5 messages in the last 65 seconds'. If you look at the count of 5 like an ABSOLUTE data type 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 an absolute amount by multiplying by the delta-time between the points. RRDtool plots continuous data, and as such is not appropriate for plotting absolute amounts as for example "total bytes" sent and received in a router. What you probably want is plot rates that you can scale to bytes/hour, for example, or plot absolute amounts 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).


EXAMPLE

 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 RRD called temperature.rrd which accepts one temperature value every 300 seconds. If no new data is supplied for more than 600 seconds, the temperature becomes *UNKNOWN*. The minimum acceptable value is -273 and the maximum is 5'000.

A few archive areas are also defined. The first stores the temperatures supplied for 100 hours (1'200 * 300 seconds = 100 hours). The second RRA stores the minimum temperature recorded over every hour (12 * 300 seconds = 1 hour), for 100 days (2'400 hours). The third and the fourth RRA's do the same for the maximum and average temperature, respectively.


EXAMPLE 2

 rrdtool create monitor.rrd --step 300        \
   DS:ifOutOctets:COUNTER:1800:0:4294967295   \
   RRA:AVERAGE:0.5:1:2016                     \
   RRA:HWPREDICT:1440:0.1:0.0035:288

This example is a monitor of a router interface. The first RRA tracks the traffic flow in octets; the second RRA generates the specialized functions RRAs for aberrant behavior detection. Note that the rra-num argument of HWPREDICT is missing, so the other RRAs will implicitly be created with default parameter values. In this example, the forecasting algorithm baseline adapts quickly; in fact the most recent one hour of observations (each at 5 minute intervals) accounts for 75% of the baseline prediction. The linear trend forecast adapts much more slowly. Observations made during the last day (at 288 observations per day) account for only 65% of the predicted linear trend. Note: these computations rely on an exponential smoothing formula described in the LISA 2000 paper.

The seasonal cycle is one day (288 data points at 300 second intervals), and the seasonal adaption parameter will be set to 0.1. The RRD file will store 5 days (1'440 data points) of forecasts and deviation predictions before wrap around. The file will store 1 day (a seasonal cycle) of 0-1 indicators in the FAILURES RRA.

The same RRD file and RRAs are created with the following command, which explicitly creates all specialized function RRAs.

 rrdtool create monitor.rrd --step 300 \
   DS:ifOutOctets:COUNTER:1800:0:4294967295 \
   RRA:AVERAGE:0.5:1:2016 \
   RRA:HWPREDICT:1440:0.1:0.0035:288:3 \
   RRA:SEASONAL:288:0.1:2 \
   RRA:DEVPREDICT:1440:5 \
   RRA:DEVSEASONAL:288:0.1:2 \
   RRA:FAILURES:288:7:9:5

Of course, explicit creation need not replicate implicit create, a number of arguments could be changed.


EXAMPLE 3

 rrdtool create proxy.rrd --step 300 \
   DS:Total:DERIVE:1800:0:U  \
   DS:Duration:DERIVE:1800:0:U  \
   DS:AvgReqDur:COMPUTE:Duration,Requests,0,EQ,1,Requests,IF,/ \
   RRA:AVERAGE:0.5:1:2016

This example is monitoring the average request duration during each 300 sec interval for requests processed by a web proxy during the interval. In this case, the proxy exposes two counters, the number of requests processed since boot and the total cumulative duration of all processed requests. Clearly these counters both have some rollover point, but using the DERIVE data source also handles the reset that occurs when the web proxy is stopped and restarted.

In the RRD, the first data source stores the requests per second rate during the interval. The second data source stores the total duration of all requests processed during the interval divided by 300. The COMPUTE data source divides each PDP of the AccumDuration by the corresponding PDP of TotalRequests and stores the average request duration. The remainder of the RPN expression handles the divide by zero case.


AUTHOR

Tobias Oetiker <tobi@oetiker.ch>

rrdtool-1.4.7/doc/rrdlastupdate.html0000644000175300017510000000530111707501545014452 00000000000000 rrdlastupdate

NAME

rrdlastupdate - Return the most recent update to an RRD


SYNOPSIS

rrdtool lastupdate filename [--daemon address]


DESCRIPTION

The lastupdate function returns the UNIX timestamp and the value stored for each datum in the most recent update of an RRD.

filename

The name of the RRD that contains the data.

--daemon address

Address of the the rrdcached manpage daemon. If specified, a flush command is sent to the server before reading the RRD files. This allows rrdtool to return fresh data even if the daemon is configured to cache values for a long time. For a list of accepted formats, see the -l option in the the rrdcached manpage manual.

 rrdtool lastupdate --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd


ENVIRONMENT VARIABLES

The following environment variables may be used to change the behavior of rrdtoollastupdate:

RRDCACHED_ADDRESS

If this environment variable is set it will have the same effect as specifying the --daemon option on the command line. If both are present, the command line argument takes precedence.


AUTHOR

Andy Riebs <andy.riebs@hp.com>

rrdtool-1.4.7/doc/rrdlast.html0000644000175300017510000000520411707501545013251 00000000000000 rrdlast

NAME

rrdlast - Return the date of the last data sample in an RRD


SYNOPSIS

rrdtool last filename [--daemon address]


DESCRIPTION

The last function returns the UNIX timestamp of the most recent update of the RRD.

filename

The name of the RRD that contains the data.

--daemon address

Address of the the rrdcached manpage daemon. If specified, a flush command is sent to the server before reading the RRD files. This allows rrdtool to return fresh data even if the daemon is configured to cache values for a long time. For a list of accepted formats, see the -l option in the the rrdcached manpage manual.

 rrdtool last --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd


ENVIRONMENT VARIABLES

The following environment variables may be used to change the behavior of rrdtoollast:

RRDCACHED_ADDRESS

If this environment variable is set it will have the same effect as specifying the --daemon option on the command line. If both are present, the command line argument takes precedence.


AUTHOR

Russ Wright <rwwright@home.com>

rrdtool-1.4.7/doc/rrdgraph.html0000644000175300017510000006731311707501545013420 00000000000000 rrdgraph

NAME

rrdgraph - Round Robin Database tool graphing functions


SYNOPSIS

rrdtool graph|graphv filename [option ...] [data definition ...] [data calculation ...] [variable definition ...] [graph element ...] [print element ...]


DESCRIPTION

The graph function of RRDtool is used to present the data from an RRD to a human viewer. Its main purpose is to create a nice graphical representation, but it can also generate a numerical report.


OVERVIEW

rrdtool graph needs data to work with, so you must use one or more data definition statements to collect this data. You are not limited to one database, it's perfectly legal to collect data from two or more databases (one per statement, though).

If you want to display averages, maxima, percentiles, etcetera it is best to collect them now using the variable definition statement. Currently this makes no difference, but in a future version of RRDtool you may want to collect these values before consolidation.

The data fetched from the RRA is then consolidated so that there is exactly one data point per pixel in the graph. If you do not take care yourself, RRDtool will expand the range slightly if necessary. Note, in that case the first and/or last pixel may very well become unknown!

Sometimes data is not exactly in the format you would like to display it. For instance, you might be collecting bytes per second, but want to display bits per second. This is what the data calculation command is designed for. After consolidating the data, a copy is made and this copy is modified using a rather powerful RPN command set.

When you are done fetching and processing the data, it is time to graph it (or print it). This ends the rrdtool graph sequence.

Use graphv instead of graph to get detailed information about the graph geometry and data once it is drawn. See the bottom of the document for more information.


OPTIONS

filename

The name and path of the graph to generate. It is recommended to end this in .png, .svg or .eps, but RRDtool does not enforce this.

filename can be '-' to send the image to stdout. In this case, no other output is generated.

Time range

[-s|--start time] [-e|--end time] [-S|--step seconds]

The start and end of the time series you would like to display, and which RRA the data should come from. Defaults are: 1 day ago until now, with the best possible resolution. Start and end can be specified in several formats, see AT-STYLE TIME SPECIFICATION and the rrdgraph_examples manpage. By default, rrdtool graph calculates the width of one pixel in the time domain and tries to get data from an RRA with that resolution. With the step option you can alter this behavior. If you want rrdtool graph to get data at a one-hour resolution from the RRD, set step to 3'600. Note: a step smaller than one pixel will silently be ignored.

Labels

[-t|--title string] [-v|--vertical-label string]

A horizontal string at the top of the graph and/or a vertically placed string at the left hand side of the graph.

Size

[-w|--width pixels] [-h|--height pixels] [-j|--only-graph] [-D|--full-size-mode]

By default, the width and height of the canvas (the part with the actual data and such). This defaults to 400 pixels by 100 pixels.

If you specify the --full-size-mode option, the width and height specify the final dimensions of the output image and the canvas is automatically resized to fit.

If you specify the --only-graph option and set the height < 32 pixels you will get a tiny graph image (thumbnail) to use as an icon for use in an overview, for example. All labeling will be stripped off the graph.

Limits

[-u|--upper-limit value] [-l|--lower-limit value] [-r|--rigid]

By default the graph will be autoscaling so that it will adjust the y-axis to the range of the data. You can change this behavior by explicitly setting the limits. The displayed y-axis will then range at least from lower-limit to upper-limit. Autoscaling will still permit those boundaries to be stretched unless the rigid option is set.

[-A|--alt-autoscale]

Sometimes the default algorithm for selecting the y-axis scale is not satisfactory. Normally the scale is selected from a predefined set of ranges and this fails miserably when you need to graph something like 260 + 0.001 * sin(x). This option calculates the minimum and maximum y-axis from the actual minimum and maximum data values. Our example would display slightly less than 260-0.001 to slightly more than 260+0.001 (this feature was contributed by Sasha Mikheev).

[-J|--alt-autoscale-min]

Where --alt-autoscale will modify both the absolute maximum AND minimum values, this option will only affect the minimum value. The maximum value, if not defined on the command line, will be 0. This option can be useful when graphing router traffic when the WAN line uses compression, and thus the throughput may be higher than the WAN line speed.

[-M|--alt-autoscale-max]

Where --alt-autoscale will modify both the absolute maximum AND minimum values, this option will only affect the maximum value. The minimum value, if not defined on the command line, will be 0. This option can be useful when graphing router traffic when the WAN line uses compression, and thus the throughput may be higher than the WAN line speed.

[-N|--no-gridfit]

In order to avoid anti-aliasing blurring effects RRDtool snaps points to device resolution pixels, this results in a crisper appearance. If this is not to your liking, you can use this switch to turn this behavior off.

Grid-fitting is turned off for PDF, EPS, SVG output by default.

X-Axis

[-x|--x-grid GTM:GST:MTM:MST:LTM:LST:LPR:LFM]

[-x|--x-grid none]

The x-axis label is quite complex to configure. If you don't have very special needs it is probably best to rely on the auto configuration to get this right. You can specify the string none to suppress the grid and labels altogether.

The grid is defined by specifying a certain amount of time in the ?TM positions. You can choose from SECOND, MINUTE, HOUR, DAY, WEEK, MONTH or YEAR. Then you define how many of these should pass between each line or label. This pair (?TM:?ST) needs to be specified for the base grid (G??), the major grid (M??) and the labels (L??). For the labels you also must define a precision in LPR and a strftime format string in LFM. LPR defines where each label will be placed. If it is zero, the label will be placed right under the corresponding line (useful for hours, dates etcetera). If you specify a number of seconds here the label is centered on this interval (useful for Monday, January etcetera).

 --x-grid MINUTE:10:HOUR:1:HOUR:4:0:%X

This places grid lines every 10 minutes, major grid lines every hour, and labels every 4 hours. The labels are placed under the major grid lines as they specify exactly that time.

 --x-grid HOUR:8:DAY:1:DAY:1:86400:%A

This places grid lines every 8 hours, major grid lines and labels each day. The labels are placed exactly between two major grid lines as they specify the complete day and not just midnight.

Y-Axis

[-y|--y-grid grid step:label factor]

[-y|--y-grid none]

Y-axis grid lines appear at each grid step interval. Labels are placed every label factor lines. You can specify -y none to suppress the grid and labels altogether. The default for this option is to automatically select sensible values.

If you have set --y-grid to 'none' not only the labels get suppressed, also the space reserved for the labels is removed. You can still add space manually if you use the --units-length command to explicitly reserve space.

[-Y|--alt-y-grid]

Place the Y grid dynamically based on the graph's Y range. The algorithm ensures that you always have a grid, that there are enough but not too many grid lines, and that the grid is metric. That is the grid lines are placed every 1, 2, 5 or 10 units. This parameter will also ensure that you get enough decimals displayed even if your graph goes from 69.998 to 70.001. (contributed by Sasha Mikheev).

[-o|--logarithmic]

Logarithmic y-axis scaling.

[-X|--units-exponent value]

This sets the 10**exponent scaling of the y-axis values. Normally, values will be scaled to the appropriate units (k, M, etc.). However, you may wish to display units always in k (Kilo, 10e3) even if the data is in the M (Mega, 10e6) range, for instance. Value should be an integer which is a multiple of 3 between -18 and 18 inclusively. It is the exponent on the units you wish to use. For example, use 3 to display the y-axis values in k (Kilo, 10e3, thousands), use -6 to display the y-axis values in u (Micro, 10e-6, millionths). Use a value of 0 to prevent any scaling of the y-axis values.

This option is very effective at confusing the heck out of the default RRDtool autoscaling function and grid painter. If RRDtool detects that it is not successful in labeling the graph under the given circumstances, it will switch to the more robust --alt-y-grid mode.

[-L|--units-length value]

How many digits should RRDtool assume the y-axis labels to be? You may have to use this option to make enough space once you start fiddling with the y-axis labeling.

[--units=si]

With this option y-axis values on logarithmic graphs will be scaled to the appropriate units (k, M, etc.) instead of using exponential notation. Note that for linear graphs, SI notation is used by default.

Right Y Axis

[--right-axis scale:shift] [--right-axis-label label]

A second axis will be drawn to the right of the graph. It is tied to the left axis via the scale and shift parameters. You can also define a label for the right axis.

[--right-axis-format format-string]

By default the format of the axis labels gets determined automatically. If you want to do this your self, use this option with the same %lf arguments you know from the PRINT and GPRINT commands.

Legend

[-g|--no-legend]

Suppress generation of the legend; only render the graph.

[-F|--force-rules-legend]

Force the generation of HRULE and VRULE legends even if those HRULE or VRULE will not be drawn because out of graph boundaries (mimics behavior of pre 1.0.42 versions).

[--legend-position=(north|south|west|east)]

Place the legend at the given side of the graph. The default is south. In west or east position it is necessary to add line breaks manually.

[--legend-direction=(topdown|bottomup)]

Place the legend items in the given vertical order. The default is topdown. Using bottomup the legend items appear in the same vertical order as a stack of lines or areas.

Miscellaneous

[-z|--lazy]

Only generate the graph if the current graph is out of date or not existent. Note, that all the calculations will happen regardless so that the output of PRINT and graphv will be complete regardless. Note that the behavior of lazy in this regard has seen several changes over time. The only thing you can really rely on before RRDtool 1.3.7 is that lazy will not generate the graph when it is already there and up to date, and also that it will output the size of the graph.

[--daemon address]

Address of the the rrdcached manpage daemon. If specified, a flush command is sent to the server before reading the RRD files. This allows the graph to contain fresh data even if the daemon is configured to cache values for a long time. For a list of accepted formats, see the -l option in the the rrdcached manpage manual.

 rrdtool graph [...] --daemon unix:/var/run/rrdcached.sock [...]

[-f|--imginfo printfstr]

After the image has been created, the graph function uses printf together with this format string to create output similar to the PRINT function, only that the printf function is supplied with the parameters filename, xsize and ysize. In order to generate an IMG tag suitable for including the graph into a web page, the command line would look like this:

 --imginfo '<IMG SRC="/img/%s" WIDTH="%lu" HEIGHT="%lu" ALT="Demo">'

[-c|--color COLORTAG#rrggbb[aa]]

Override the default colors for the standard elements of the graph. The COLORTAG is one of BACK background, CANVAS for the background of the actual graph, SHADEA for the left and top border, SHADEB for the right and bottom border, GRID, MGRID for the major grid, FONT for the color of the font, AXIS for the axis of the graph, FRAME for the line around the color spots, and finally ARROW for the arrow head pointing up and forward. Each color is composed out of three hexadecimal numbers specifying its rgb color component (00 is off, FF is maximum) of red, green and blue. Optionally you may add another hexadecimal number specifying the transparency (FF is solid). You may set this option several times to alter multiple defaults.

A green arrow is made by: --color ARROW#00FF00

[--grid-dash on:off]

by default the grid is drawn in a 1 on, 1 off pattern. With this option you can set this yourself

 --grid-dash 1:3    for a dot grid
 
 --grid-dash 1:0    for uninterrupted grid lines

[--border width]]

Width in pixels for the 3d border drawn around the image. Default 2, 0 disables the border. See SHADEA and SHADEB above for setting the border color.

[--dynamic-labels]

Pick the shape of the color marker next to the label according to the element drawn on the graph.

[-m|--zoom factor]

Zoom the graphics by the given amount. The factor must be > 0

[-n|--font FONTTAG:size:[font]]

This lets you customize which font to use for the various text elements on the RRD graphs. DEFAULT sets the default value for all elements, TITLE for the title, AXIS for the axis labels, UNIT for the vertical unit label, LEGEND for the graph legend, WATERMARK for the watermark on the edge of the graph.

Use Times for the title: --font TITLE:13:Times

Note that you need to quote the argument to --font if the font-name contains whitespace: --font "TITLE:13:Some Font"

If you do not give a font string you can modify just the size of the default font: --font TITLE:13:.

If you specify the size 0 then you can modify just the font without touching the size. This is especially useful for altering the default font without resetting the default fontsizes: --font DEFAULT:0:Courier.

RRDtool comes with a preset default font. You can set the environment variable RRD_DEFAULT_FONT if you want to change this.

RRDtool uses Pango for its font handling. This means you can to use the full Pango syntax when selecting your font:

The font name has the form "[FAMILY-LIST] [STYLE-OPTIONS] [SIZE]", where FAMILY-LIST is a comma separated list of families optionally terminated by a comma, STYLE_OPTIONS is a whitespace separated list of words where each WORD describes one of style, variant, weight, stretch, or gravity, and SIZE is a decimal number (size in points) or optionally followed by the unit modifier "px" for absolute size. Any one of the options may be absent.

[-R|--font-render-mode {normal,light,mono}]

There are 3 font render modes:

normal: Full Hinting and Anti-aliasing (default)

light: Slight Hinting and Anti-aliasing

mono: Full Hinting and NO Anti-aliasing

[-B|--font-smoothing-threshold size]

(this gets ignored in 1.3 for now!)

This specifies the largest font size which will be rendered bitmapped, that is, without any font smoothing. By default, no text is rendered bitmapped.

[-P|--pango-markup]

All text in RRDtool is rendered using Pango. With the --pango-markup option, all text will be processed by pango markup. This allows to embed some simple html like markup tags using

 <span key="value">text</span>

Apart from the verbose syntax, there are also the following short tags available.

 b     Bold
 big   Makes font relatively larger, equivalent to <span size="larger">
 i     Italic
 s     Strikethrough
 sub   Subscript
 sup   Superscript
 small Makes font relatively smaller, equivalent to <span size="smaller">
 tt    Monospace font
 u     Underline

More details on http://developer.gnome.org/doc/API/2.0/pango/PangoMarkupFormat.html.

[-G|--graph-render-mode {normal,mono}]

There are 2 render modes:

normal: Graphs are fully Anti-aliased (default)

mono: No Anti-aliasing

[-E|--slope-mode]

RRDtool graphs are composed of stair case curves by default. This is in line with the way RRDtool calculates its data. Some people favor a more 'organic' look for their graphs even though it is not all that true.

[-a|--imgformat PNG|SVG|EPS|PDF]

Image format for the generated graph. For the vector formats you can choose among the standard Postscript fonts Courier-Bold, Courier-BoldOblique, Courier-Oblique, Courier, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique, Helvetica, Symbol, Times-Bold, Times-BoldItalic, Times-Italic, Times-Roman, and ZapfDingbats.

[-i|--interlaced]

(this gets ignored in 1.3 for now!)

If images are interlaced they become visible on browsers more quickly.

[-T|--tabwidth value]

By default the tab-width is 40 pixels, use this option to change it.

[-b|--base value]

If you are graphing memory (and NOT network traffic) this switch should be set to 1024 so that one Kb is 1024 byte. For traffic measurement, 1 kb/s is 1000 b/s.

[-W|--watermark string]

Adds the given string as a watermark, horizontally centered, at the bottom of the graph.

Data and variables

DEF:vname=rrdfile:ds-name:CF[:step=step][:start=time][:end=time]

CDEF:vname=RPN expression

VDEF:vname=RPN expression

You need at least one DEF and one LINE, AREA, GPRINT, PRINT statement to generate anything useful.

See the rrdgraph_data manpage and the rrdgraph_rpn manpage for the exact format.

NOTE: Graph and print elements

You need at least one graph element to generate an image and/or at least one print statement to generate a report. See the rrdgraph_graph manpage for the exact format.

graphv

Calling RRDtool with the graphv option will return information in the RRDtool info format. On the command line this means that all output will be in key=value format. When used from the Perl and Ruby bindings a hash pointer will be returned from the call.

When the filename '-' is given, the contents of the graph itself will also be returned through this interface (hash key 'image'). On the command line the output will look like this:

 print[0] = "0.020833"
 print[1] = "0.0440833"
 graph_left = 51
 graph_top = 22
 graph_width = 400
 graph_height = 100
 graph_start = 1232908800
 graph_end = 1232914200
 image_width = 481
 image_height = 154
 value_min = 0.0000000000e+00
 value_max = 4.0000000000e-02
 image = BLOB_SIZE:8196
 [... 8196 bytes of image data ...]

There is more information returned than in the standard interface. Especially the 'graph_*' keys are new. They help applications that want to know what is where on the graph.


ENVIRONMENT VARIABLES

The following environment variables may be used to change the behavior of rrdtoolgraph:

RRDCACHED_ADDRESS

If this environment variable is set it will have the same effect as specifying the --daemon option on the command line. If both are present, the command line argument takes precedence.


SEE ALSO

the rrdgraph manpage gives an overview of how rrdtool graph works. the rrdgraph_data manpage describes DEF,CDEF and VDEF in detail. the rrdgraph_rpn manpage describes the RPN language used in the ?DEF statements. the rrdgraph_graph manpage page describes all of the graph and print functions.

Make sure to read the rrdgraph_examples manpage for tips&tricks.


AUTHOR

Program by Tobias Oetiker <tobi@oetiker.ch>

This manual page by Alex van den Bogaerdt <alex@vandenbogaerdt.nl> with corrections and/or additions by several people

rrdtool-1.4.7/doc/rrdcgi.html0000644000175300017510000002367111707501544013057 00000000000000 rrdcgi

NAME

rrdcgi - Create web pages containing RRD graphs based on templates


SYNOPSIS

#!/path/to/rrdcgi [--filter]


DESCRIPTION

rrdcgi 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 <RRD:: tags. rrdcgi will interpret and act according to these tags. In the end it will printout a web page including the necessary CGI headers.

rrdcgi parses the contents of the template in 3 steps. In each step it looks only for a subset of tags. This allows nesting of tags.

The argument parser uses the same semantics as you are used from your C-shell.

--filter

Assume that rrdcgi is run as a filter and not as a cgi.

Keywords

RRD::CV name

Inserts the CGI variable of the given name.

RRD::CV::QUOTE name

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 to be one argument.

RRD::CV::PATH name

Inserts the CGI variable of the given name, quotes it and makes sure it starts neither with a '/' nor contains '..'. This is to make sure that no problematic pathnames can be introduced through the CGI interface.

RRD::GETENV variable

Get the value of an environment variable.

 <RRD::GETENV REMOTE_USER>

might give you the name of the remote user given you are using some sort of access control on the directory.

RRD::GOODFOR seconds

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 negative a Refresh header.

RRD::INCLUDE filename

Include the contents of the specified file into the page returned from the cgi.

RRD::SETENV variable value

If you want to present your graphs in another time zone than your own, you could use

 <RRD::SETENV TZ UTC>

to make sure everything is presented in Universal Time. Note that the values permitted to TZ depend on your OS.

RRD::SETVAR variable value

Analog to SETENV but for local variables.

RRD::GETVAR variable

Analog to GETENV but for local variables.

RRD::TIME::LAST rrd-file strftime-format

This gets replaced by the last modification time of the selected RRD. The time is strftime-formatted with the string specified in the second argument.

RRD::TIME::NOW strftime-format

This gets replaced by the current time of day. The time is strftime-formatted with the string specified in the argument.

Note that if you return : (colons) from your strftime format you may have to escape them using \ if the time is to be used as an argument to a GRAPH command.

RRD::TIME::STRFTIME START|END start-spec end-spec strftime-format

This gets replaced by a strftime-formatted time using the format strftime-format on either start-spec or end-spec depending on whether START or END is specified. Both start-spec and end-spec 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".

Note that again, if you return : (colon) from your strftime format, you may have to escape them using \ if the time is to be used as an argument to a GRAPH command.

RRD::GRAPH rrdgraph arguments

This tag creates the RRD graph defined by its argument and then is replaced by an appropriate <IMG ... > tag referring to the graph. The --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 RRD::GRAPH tag work as described in the rrdgraph manual page.

Use the --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 --imginfo format, the following will be used:

 <IMG SRC="%s" WIDTH="%lu" HEIGHT="%lu">

Note that %s stands for the filename part of the graph generated, all directories given in the PNG file argument will get dropped.

RRD::PRINT number

If the preceding RRD::GRAPH tag contained and PRINT arguments, then you can access their output with this tag. The number argument refers to the number of the PRINT argument. This first PRINT has number 0.

RRD::INTERNAL <var>

This tag gets replaced by an internal var. Currently these vars are known: VERSION, COMPILETIME. These vars represent the compiled-in values.


EXAMPLE 1

The example below creates a web pages with a single RRD graph.

 #!/usr/local/bin/rrdcgi
 <HTML>
 <HEAD><TITLE>RRDCGI Demo</TITLE></HEAD>
 <BODY>
 <H1>RRDCGI Example Page</H1>
 <P>
 <RRD::GRAPH demo.png --lazy --title="Temperatures"
          DEF:cel=demo.rrd:exhaust:AVERAGE
          LINE2:cel#00a000:"D. Celsius">
 </P>
 </BODY>
 </HTML>


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 as source for your graph.

 #!/usr/local/bin/rrdcgi
 <HTML>
 <HEAD><TITLE>RRDCGI Demo</TITLE></HEAD>
 <BODY>
 <H1>RRDCGI Example Page for <RRD::CV RRD_NAME></H1>
 <H2>Selection</H2>
 <FORM><INPUT NAME=RRD_NAME TYPE=RADIO VALUE=roomA> Room A,
       <INPUT NAME=RRD_NAME TYPE=RADIO VALUE=roomB> Room B.
       <INPUT TYPE=SUBMIT></FORM>
 <H2>Graph</H2>
 <P>
 <RRD::GRAPH <RRD::CV::PATH RRD_NAME>.png --lazy
          --title "Temperatures for "<RRD::CV::QUOTE RRD_NAME>
          DEF:cel=<RRD::CV::PATH RRD_NAME>.rrd:exhaust:AVERAGE
          LINE2:cel#00a000:"D. Celsius">
 </P>
 </BODY>
 </HTML>


EXAMPLE 3

This example shows how to handle the case where the RRD, graphs and cgi-bins are separate directories

 #!/.../bin/rrdcgi
 <HTML>
 <HEAD><TITLE>RRDCGI Demo</TITLE></HEAD>
 <BODY>
 <H1>RRDCGI test Page</H1>
 <RRD::GRAPH
  /.../web/pngs/testhvt.png
  --imginfo '<IMG SRC=/.../pngs/%s WIDTH=%lu HEIGHT=%lu >'
  --lazy --start -1d --end now
  DEF:http_src=/.../rrds/test.rrd:http_src:AVERAGE
  AREA:http_src#00ff00:http_src
 >
 </BODY>
 </HTML>

Note 1: Replace /.../ with the relevant directories

Note 2: The SRC=/.../pngs should be paths from the view of the webserver/browser


AUTHOR

Tobias Oetiker <tobi@oetiker.ch>

rrdtool-1.4.7/doc/rrdflushcached.html0000644000175300017510000000552411707501544014563 00000000000000 rrdflushcached

NAME

rrdflushcached - Flush the values for a spcific RRD file from memory.


SYNOPSIS

rrdtool flushcached [--daemon address] filename [filename ...]


DESCRIPTION

The flushcached function connects to the rrdcached manpage, the RRD caching daemon, and issues a "flush" command for the given files. The daemon will put the files to the head of the update queue so they are written "soon". The status will be returned only after the files' pending updates have been written to disk.

filename

The name(s) of the RRD file(s) that are to be written to disk.

--daemon address

Address of the the rrdcached manpage daemon. If not specified, the RRDCACHED_ADDRESS environment variable must be set (see below). For a list of accepted formats, see the -l option in the the rrdcached manpage manual.

 rrdtool flush --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd


ENVIRONMENT VARIABLES

The following environment variables may be used to change the behavior of rrdtoolflushcached:

RRDCACHED_ADDRESS

If this environment variable is set it will have the same effect as specifying the --daemon option on the command line. If both are present, the command line argument takes precedence.


AUTHOR

Florian Forster <octo at verplant.org>

rrdtool-1.4.7/doc/rrdbuild.html0000644000175300017510000003737311707501544013420 00000000000000 rrdbuild

NAME

rrdbuild - Instructions for building RRDtool


OVERVIEW

If you downloaded the source of RRDtool you have to compile it. This document will give some information on how this is done.

RRDtool relies on services of third part libraries. Some of these libraries may already be installed on your system. You have to compile copies of the other ones before you can build RRDtool.

This document will tell you about all the necessary steps to get going.

These instructions assume you are using a bash shell. If you use csh/tcsh, then you can either type bash to switch to bash for the compilation or if you know what you are doing just replace the export bits with setenv.

We further assume that your copies of tar and make are actually GNU tar and GNU make respectively. It could be that they are installed as gtar and gmake on your system.


OPTIMISTIC BUILD

Before you start to build RRDtool, you have to decide two things:

  1. In which directory you want to build the software.

  2. Where you want to install the software.

Once you have decided. Save the two locations into environment variables.

 BUILD_DIR=/tmp/rrdbuild
 INSTALL_DIR=/opt/rrdtool-1.4.7

If your /tmp is mounted with the option noexec (RHEL seems todo that) you have to choose a different directory!

Now make sure the BUILD_DIR exists and go there:

 mkdir -p $BUILD_DIR
 cd $BUILD_DIR

Lets first assume you already have all the necessary libraries pre-installed.

 wget http://oss.oetiker.ch/rrdtool/pub/rrdtool-1.4.7.tar.gz
 gunzip -c rrdtool-1.4.7.tar.gz | tar xf -
 cd rrdtool-1.4.7
 ./configure --prefix=$INSTALL_DIR && make && make install

Ok, this was very optimistic. This try will probably have ended with configure complaining about several missing libraries.


INSTALLING DEPENDENCIES

If your OS lets you install additional packages from a software repository, you may get away with installing the missing packages. When the packages are installed, run configure again and try to compile again. Below you find some hints on getting your OS ready for compiling RRDtool.

Additions to this list are welcome. In general RRDtool should work with the latest versions of the libraries. The versions listed here are just what was current when I tested this.

OpenSolaris 2008.05

Just add a compiler and the gnome development package:

 pkg install sunstudioexpress
 pkg install SUNWgnome-common-devel

There is a problem with cairo.pc on OpenSolaris. It suggests that xrender is required for compilation with cairo. This is not true and also bad since OpenSolaris does not include an xrender.pc file. Use Perl to fix this:

 perl -i~ -p -e 's/(Requires.*?)\s*xrender.*/$1/' /usr/lib/pkgconfig/cairo.pc

Make sure the RRDtool build system finds your new compiler

 export PATH=/opt/SunStudioExpress/bin

Debian / Ubuntu

Use apt-get to make sure you have all that is required. A number of packages will get added through dependencies.

 apt-get install libpango1.0-dev libxml2-dev

Gentoo

In Gentoo installing RRDtool is really simple you just need to emerge rrdtool. All dependencies will be handled automatically by the portage system. The only thing you should care about are USE flags, which allow you fine tune features RRDtool will be built with. Currently the following USE flags are available:

 doc    - install .html and .txt documentation
          into /usr/share/doc/rrdtool-1.x.xx/
 perl   - build and install perl language bindings
 python - build and install python language bindings
 ruby   - build and install ruby language bindings
 tcl    - build and install tcl language bindings
 rrdcgi - build and install rrdcgi

After you've decided which USE flags you need, set them either in make.conf or /etc/portage/package.use and finally run:

 # emerge -va rrdtool

Take a look at Gentoo handbook for further details on how to manage USE flags: http://www.gentoo.org/doc/en/handbook/handbook-x86.xml?part=2


BUILDING DEPENDENCIES

But again this may have been too optimistic still, and you actually have to compile your own copies of some of the required libraries. Things like libpng and zlib are pretty standard so you will probably have them on your system anyway. Freetype, Fontinst, Cairo, Pango may be installed, but it is possible that they are pretty old and thus don't live up to our expectations, so you may want to compile their latest versions.

General build tips for AIX

If you are working with AIX, you may find the --disable-shared option will cause things to break for you. In that case you may have to install the shared libraries into the RRDtool PREFIX and work with --disable-static instead.

Another hint to get RRDtool working on AIX is to use the IBM XL C Compiler:

 export CC=/usr/vac/bin/cc
 export PERLCC=$CC

(Better instructions for AIX welcome!)

Build Instructions

Some libraries want to know where other libraries are. For this to work, set the following environment variable

 export PKG_CONFIG_PATH=${INSTALL_DIR}/lib/pkgconfig
 export PATH=$INSTALL_DIR/bin:$PATH

The above relies on the presence of the pkgconfig program. Below you find instructions on how to compile pkgconfig.

Since we are compiling libraries dynamically, they must know where to find each other. This is done by setting an appropriate LDFLAGS. Unfortunately, the syntax again differs from system to system:

Solaris
 export LDFLAGS=-R${INSTALL_DIR}/lib

if you are using the Sun Studio/Forte compiler, you may also want to set

 CFLAGS="-xO3 -xcode=pic13"   (SPARC)
 CFLAGS="-xO3 -Kpic"          (x86)
Linux
 export LDFLAGS="-Wl,--rpath -Wl,${INSTALL_DIR}/lib"
HPUX
 export LDFLAGS="+b${INSTALL_DIR}/lib"
AIX
 export LDFLAGS="-Wl,-blibpath:${INSTALL_DIR}/lib"

If you have GNU make installed and it is not called 'make', then do

 export MAKE=gmake
 export GNUMAKE=gmake

otherwise just do

 export MAKE=make

Building pkgconfig

As mentioned above, without pkgconfig the whole build process will be lots of pain and suffering, so make sure you have a copy on your system. If it is not available natively, here is how to compile it.

 wget http://pkgconfig.freedesktop.org/releases/pkg-config-0.23.tar.gz
 gunzip -c pkg-config-0.23.tar.gz | tar xf -
 cd pkg-config-0.23
 ./configure --prefix=$INSTALL_DIR CFLAGS="-O3 -fPIC"
 $MAKE
 $MAKE install

After installing pkgconfig in a custom directory, setting up the corresponding environment variable will be helpful.

 export PKG_CONFIG=$INSTALL_DIR/bin/pkg-config

Building zlib

Chances are very high that you already have that on your system ...

 cd $BUILD_DIR
 wget http://oss.oetiker.ch/rrdtool/pub/libs/zlib-1.2.3.tar.gz
 gunzip -c zlib-1.2.3.tar.gz | tar xf -
 cd zlib-1.2.3
 ./configure --prefix=$INSTALL_DIR CFLAGS="-O3 -fPIC" --shared
 $MAKE
 $MAKE install

Building libpng

Libpng itself requires zlib to build, so we need to help a bit. If you already have a copy of zlib on your system (which is very likely) you can drop the settings of LDFLAGS and CPPFLAGS. Note that the backslash (\) at the end of line 4 means that line 4 and line 5 are on one line.

 cd $BUILD_DIR
 wget http://oss.oetiker.ch/rrdtool/pub/libs/libpng-1.2.18.tar.gz
 gunzip -c libpng-1.2.18.tar.gz | tar xf -
 cd libpng-1.2.10
 env CFLAGS="-O3 -fPIC" ./configure --prefix=$INSTALL_DIR
 $MAKE
 $MAKE install

Building freetype

 cd $BUILD_DIR
 wget http://oss.oetiker.ch/rrdtool/pub/libs/freetype-2.3.5.tar.gz
 gunzip -c freetype-2.3.5.tar.gz | tar xf -
 cd freetype-2.3.5
 ./configure --prefix=$INSTALL_DIR CFLAGS="-O3 -fPIC"
 $MAKE
 $MAKE install

If you run into problems building freetype on Solaris, you may want to try to add the following at the start the configure line:

 env EGREP=egrep

Building LibXML2

 cd $BUILD_DIR
 wget http://oss.oetiker.ch/rrdtool/pub/libs/libxml2-2.6.32.tar.gz
 gunzip -c libxml2-2.6.32.tar.gz | tar xf -
 cd libxml2-2.6.32
 ./configure --prefix=$INSTALL_DIR CFLAGS="-O3 -fPIC"
 $MAKE
 $MAKE install

Building fontconfig

Note that fontconfig has a run time configuration file in INSTALL_DIR/etc you may want to adjust that so that fontconfig finds the fonts on your system. Run the fc-cache program to build the fontconfig cache after changing the config file.

 cd $BUILD_DIR
 wget http://oss.oetiker.ch/rrdtool/pub/libs/fontconfig-2.4.2.tar.gz
 gunzip -c fontconfig-2.4.2.tar.gz   | tar xf -
 cd fontconfig-2.4.2
 ./configure --prefix=$INSTALL_DIR CFLAGS="-O3 -fPIC" --with-freetype-config=$INSTALL_DIR/bin/freetype-config
 $MAKE
 $MAKE install

Building Pixman

 cd $BUILD_DIR
 wget http://oss.oetiker.ch/rrdtool/pub/libs/pixman-0.10.0.tar.gz
 gunzip -c pixman-0.10.0.tar.gz  | tar xf -
 cd pixman-0.10.0
 ./configure --prefix=$INSTALL_DIR CFLAGS="-O3 -fPIC"
 $MAKE
 $MAKE install

Building Cairo

 cd $BUILD_DIR
 wget http://oss.oetiker.ch/rrdtool/pub/libs/cairo-1.6.4.tar.gz
 gunzip -c cairo-1.6.4.tar.gz   | tar xf -
 cd cairo-1.6.4
 ./configure --prefix=$INSTALL_DIR \
    --enable-xlib=no \
    --enable-xlib-render=no \
    --enable-win32=no \
    CFLAGS="-O3 -fPIC"
 $MAKE
 $MAKE install

When building on Solaris you may want todo

 ./configure --prefix=$INSTALL_DIR \
    --enable-xlib=no \
    --enable-xlib-render=no \
    --enable-win32=no \
    CFLAGS="-O3 -fPIC -D_POSIX_PTHREAD_SEMANTICS"

Building Glib

 cd $BUILD_DIR
 wget http://oss.oetiker.ch/rrdtool/pub/libs/glib-2.15.4.tar.gz
 gunzip -c glib-2.15.4.tar.gz  | tar xf -
 cd glib-2.15.4
 ./configure --prefix=$INSTALL_DIR CFLAGS="-O3 -fPIC"
 $MAKE
 $MAKE install

Building Pango

 cd $BUILD_DIR
 wget http://oss.oetiker.ch/rrdtool/pub/libs/pango-1.21.1.tar.bz2
 bunzip2 -c pango-1.21.1.tar.bz2 | tar xf -
 cd pango-1.21.1
 ./configure --prefix=$INSTALL_DIR CFLAGS="-O3 -fPIC" --without-x
 $MAKE
 $MAKE install

Building rrdtool (second try)

Now all the dependent libraries are built and you can try again. This time you tell configure where it should be looking for libraries and include files. This is done via environment variables. Depending on the shell you are running, the syntax for setting environment variables is different.

And finally try building again. We disable the python and tcl bindings because it seems that a fair number of people have ill configured python and tcl setups that would prevent RRDtool from building if they are included in their current state.

 cd $BUILD_DIR/rrdtool-1.4.7
 ./configure --prefix=$INSTALL_DIR --disable-tcl --disable-python
 $MAKE clean
 $MAKE
 $MAKE install

SOLARIS HINT: if you want to build the Perl module for the native Perl (the one shipping with Solaris) you will need the Sun Forte compiler installed on your box or you have to hand-tune bindings/perl-shared/Makefile while building!

Now go to $INSTALL_DIR/share/rrdtool/examples/ and run them to see if your build has been successful.


AUTHOR

Tobias Oetiker <tobi@oetiker.ch>

rrdtool-1.4.7/doc/rrdtune.html0000644000175300017510000002574111707501544013270 00000000000000 rrdtune

NAME

rrdtune - Modify some basic properties of a Round Robin Database


SYNOPSIS

rrdtool tune filename [--heartbeat|-h ds-name:heartbeat] [--minimum|-i ds-name:min] [--maximum|-a ds-name:max] [--data-source-type|-d ds-name:DST] [--data-source-rename|-r old-name:new-name] [--deltapos scale-value] [--deltaneg scale-value] [--failure-threshold failure-threshold] [--window-length window-length] [--alpha adaption-parameter] [--beta adaption-parameter] [--gamma adaption-parameter] [--gamma-deviation adaption-parameter] [--smoothing-window fraction-of-season] [--smoothing-window-deviation fraction-of-season] [--aberrant-reset ds-name]


DESCRIPTION

The tune option allows you to alter some of the basic configuration values stored in the header area of a Round Robin Database (RRD).

One application of the tune function is to relax the validation rules on an RRD. This allows to fill a new RRD with data available in larger intervals than what you would normally want to permit. Be very careful with tune operations for COMPUTE data sources. Setting the min, max, and heartbeat for a COMPUTE data source without changing the data source type to a non-COMPUTE DST WILL corrupt the data source header in the RRD.

A second application of the tune function is to set or alter parameters used by the specialized function RRAs for aberrant behavior detection.

filename

The name of the RRD you want to tune.

--heartbeat|-h ds-name:heartbeat

modify the heartbeat of a data source. By setting this to a high value the RRD will accept things like one value per day.

--minimum|-i ds-name:min

alter the minimum value acceptable as input from the data source. Setting min to 'U' will disable this limit.

--maximum|-a ds-name:max

alter the maximum value acceptable as input from the data source. Setting max to 'U' will disable this limit.

--data-source-type|-d ds-name:DST

alter the type DST of a data source.

--data-source-rename|-r old-name:new-name

rename a data source.

--deltapos scale-value

Alter the deviation scaling factor for the upper bound of the confidence band used internally to calculate violations for the FAILURES RRA. The default value is 2. Note that this parameter is not related to graphing confidence bounds which must be specified as a CDEF argument to generate a graph with confidence bounds. The graph scale factor need not to agree with the value used internally by the FAILURES RRA.

--deltaneg scale-value

Alter the deviation scaling factor for the lower bound of the confidence band used internally to calculate violations for the FAILURES RRA. The default value is 2. As with --deltapos, this argument is unrelated to the scale factor chosen when graphing confidence bounds.

--failure-threshold failure-threshold

Alter the number of confidence bound violations that constitute a failure for purposes of the FAILURES RRA. This must be an integer less than or equal to the window length of the FAILURES RRA. This restriction is not verified by the tune option, so one can reset failure-threshold and window-length simultaneously. Setting this option will reset the count of violations to 0.

--window-length window-length

Alter the number of time points in the temporal window for determining failures. This must be an integer greater than or equal to the window length of the FAILURES RRA and less than or equal to 28. Setting this option will reset the count of violations to 0.

--alpha adaption-parameter

Alter the intercept adaptation parameter for the Holt-Winters forecasting algorithm. This parameter must be between 0 and 1.

--beta adaption-parameter

Alter the slope adaptation parameter for the Holt-Winters forecasting algorithm. This parameter must be between 0 and 1.

--gamma adaption-parameter

Alter the seasonal coefficient adaptation parameter for the SEASONAL RRA. This parameter must be between 0 and 1.

--gamma-deviation adaption-parameter

Alter the seasonal deviation adaptation parameter for the DEVSEASONAL RRA. This parameter must be between 0 and 1.

--smoothing-window fraction-of-season

Alter the size of the smoothing window for the SEASONAL RRA. This must be between 0 and 1.

--smoothing-window-deviation fraction-of-season

Alter the size of the smoothing window for the DEVSEASONAL RRA. This must be between 0 and 1.

--aberrant-reset ds-name

This option causes the aberrant behavior detection algorithm to reset for the specified data source; that is, forget all it is has learnt so far. Specifically, for the HWPREDICT or MHWPREDICT RRA, it sets the intercept and slope coefficients to unknown. For the SEASONAL RRA, it sets all seasonal coefficients to unknown. For the DEVSEASONAL RRA, it sets all seasonal deviation coefficients to unknown. For the FAILURES RRA, it erases the violation history. Note that reset does not erase past predictions (the values of the HWPREDICT or MHWPREDICT RRA), predicted deviations (the values of the DEVPREDICT RRA), or failure history (the values of the FAILURES RRA). This option will function even if not all the listed RRAs are present.

Due to the implementation of this option, there is an indirect impact on other data sources in the RRD. A smoothing algorithm is applied to SEASONAL and DEVSEASONAL values on a periodic basis. During bootstrap initialization this smoothing is deferred. For efficiency, the implementation of smoothing is not data source specific. This means that utilizing reset for one data source will delay running the smoothing algorithm for all data sources in the file. This is unlikely to have serious consequences, unless the data being collected for the non-reset data sources is unusually volatile during the reinitialization period of the reset data source.

Use of this tuning option is advised when the behavior of the data source time series changes in a drastic and permanent manner.


EXAMPLE 1

rrdtool tune data.rrd -h in:100000 -h out:100000 -h through:100000

Set the minimum required heartbeat for data sources 'in', 'out' and 'through' to 10'000 seconds which is a little over one day in data.rrd. This would allow to feed old data from MRTG-2.0 right into RRDtool without generating *UNKNOWN* entries.


EXAMPLE 2

rrdtool tune monitor.rrd --window-length 5 --failure-threshold 3

If the FAILURES RRA is implicitly created, the default window-length is 9 and the default failure-threshold is 7. This command now defines a failure as 3 or more violations in a temporal window of 5 time points.


AUTHOR

Tobias Oetiker <tobi@oetiker.ch>

rrdtool-1.4.7/doc/rrdinfo.html0000644000175300017510000000767311707501544013254 00000000000000 rrdinfo

NAME

rrdinfo - extract header information from an RRD


SYNOPSIS

rrdtool info filename [--daemon address]


DESCRIPTION

The info function prints the header information from an RRD in a parsing friendly format.

Check the rrdcreate manpage if you are uncertain about the meaning of the individual keys.

filename

The name of the RRD you want to examine.

--daemon address

Address of the the rrdcached manpage daemon. If specified, a flush command is sent to the server before reading the RRD files. This allows rrdtool to return fresh data even if the daemon is configured to cache values for a long time. For a list of accepted formats, see the -l option in the the rrdcached manpage manual.

 rrdtool info --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd


EXAMPLE

This is the output generated by running info on a simple RRD which contains two data sources and one RRA. Note that the number after the last_update keyword is in seconds since 1970. The string NaN stands for *UNKNOWN* data. In the example it means that this RRD has neither minimum nor maximum values defined for either of its data sources.

 filename = "random.rrd"
 rrd_version = "0001"
 step = 300
 last_update = 955892996
 header_size = 2872
 ds[a].type = "GAUGE"
 ds[a].minimal_heartbeat = 600
 ds[a].min = NaN
 ds[a].max = NaN
 ds[a].last_ds = "UNKN"
 ds[a].value = 2.1824421548e+04
 ds[a].unknown_sec = 0
 ds[b].type = "GAUGE"
 ds[b].minimal_heartbeat = 600
 ds[b].min = NaN
 ds[b].max = NaN
 ds[b].last_ds = "UNKN"
 ds[b].value = 3.9620838224e+03
 ds[b].unknown_sec = 0
 rra[0].cf = "AVERAGE"
 rra[0].pdp_per_row = 1
 rra[0].cdp_prep[0].value = nan
 rra[0].cdp_prep[0].unknown_datapoints = 0
 rra[0].cdp_prep[1].value = nan
 rra[0].cdp_prep[1].unknown_datapoints = 0


ENVIRONMENT VARIABLES

The following environment variables may be used to change the behavior of rrdtoolinfo:

RRDCACHED_ADDRESS

If this environment variable is set it will have the same effect as specifying the --daemon option on the command line. If both are present, the command line argument takes precedence.


AUTHOR

Tobias Oetiker <tobi@oetiker.ch>

rrdtool-1.4.7/doc/rrd-beginners.html0000644000175300017510000004253411707501544014345 00000000000000 rrd-beginners

NAME

rrd-beginners - RRDtool Beginners' Guide


SYNOPSIS

Helping new RRDtool users to understand the basics of RRDtool


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 the structure of RRDtool database. This is followed by an overview of the "graph" feature of RRDtool. At the end, it has sample scripts that illustrate the usage/wrapping of RRDtool within Shell or Perl scripts.

What makes RRDtool so special?

RRDtool is GNU licensed software developed by Tobias Oetiker, a system manager at the Swiss Federal Institute of Technology. Though it is a database, there are distinct differences between RRDtool databases and other databases as listed below:

  • 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 store data and can not create graphs.

  • In case of linear databases, new data gets appended at the bottom of the database table. Thus its size keeps on increasing, whereas the 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 behavior.

  • 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.

  • 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 run at regular intervals to ensure a constant data flow to update the RRDtool database.

RRDtool is designed to store time series of data. With every data update, an associated time stamp is stored. Time is always expressed in seconds passed since epoch (01-01-1970). RRDtool can be installed on Unix as well as Windows. It comes with a command set to carry out various operations on RRD databases. This command set can be accessed from the command line, as well as from Shell or Perl scripts. The scripts act as wrappers for accessing data stored in RRDtool databases.

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 sometimes 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 target.rrd. Start time (1'023'654'125) is specified in total number of seconds since epoch (time in seconds since 01-01-1970). While updating the database, the update time is also specified. This update time MUST be large (later) then 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 step seconds so that it updates the database every step seconds.

DS (Data Source) is the actual variable which relates to the parameter on the device that is monitored. Its syntax is

 DS:variable_name:DST:heartbeat:min:max

DS is a key word. variable_name is a name under which the parameter is saved in the 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 Primary Data Point (PDP). In our example mentioned above, a new PDP is generated every 300 seconds.

Note, that if you do NOT supply new data points exactly every 300 seconds, this is not a problem, RRDtool will interpolate the data accordingly.

DST (Data Source Type) defines the type of the 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 (the difference between the current and the previous value is greater than 0). Traffic counters on a router are an ideal candidate for using COUNTER as DST. DERIVE is the same as COUNTER, but it allows negative values as well. If you want to see the rate of change in free disk space on your server, then you might want to use the DERIVE data type. ABSOLUTE also saves the rate of change, but it assumes that the previous value is set to 0. The difference between the current and the previous value is always equal to the current value. Thus it just stores the current value divided by the 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 or calculations. Memory consumption in a server is a typical example of gauge. The difference between the different types DSTs can be explained better with the 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 heartbeat. In our example, heartbeat is 600 seconds. If the database does not get a new PDP within 300 seconds, it will wait for another 300 seconds (total 600 seconds). If it doesn't receive any PDP within 600 seconds, it will save an UNKNOWN value into the database. This UNKNOWN value is a special feature of RRDtool - it is much better than to assume a missing value was 0 (zero) or any other number which might also be a valid data value. For example, the traffic flow counter on a router keeps increasing. Lets say, a value is missed for an interval and 0 is stored instead of UNKNOWN. Now when the next value becomes available, it will calculate the difference between the current value and the previous value (0) which is not correct. So, inserting the value UNKNOWN makes much more sense here.

The next two parameters are the minimum and maximum value, respectively. If the variable to be stored has predictable maximum and minimum values, this should be specified here. Any update value falling out of this range will be stored 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 step number of PDPs. This RRA will hold rows 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). This means, this RRA is an archive for one day. After 24 CDPs, CDP number 25 will replace the 1st CDP. The 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 DSs 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.

Graphical Magic

Another important feature of RRDtool is its ability to create graphs. The "graph" command uses the "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 Sources) from a database. It is also possible to show the values from more than one database in a single graph. Often, it is necessary to perform some math on the values retrieved from the 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 meaningful 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 perform 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 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 higher 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 top AREA or LINE1/2/3. Another important thing to note is that variables are plotted in the order they are defined in the graph command. Therefore 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 can be found in the graph manual.

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 scripts. Data retrieval and report generation is done using Perl scripts. These scripts are shown below:

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

Perl script (retrieves data from database and generates graphs and statistics)

 #!/usr/bin/perl -w
 # This script fetches data from target.rrd, creates a graph of memory
 # consumption on the target (Dual P3 Processor 1 GHz, 656 MB RAM)
 # call the RRD perl module
 use lib qw( /usr/local/rrdtool-1.0.41/lib/perl ../lib/perl );
 use RRDs;
 my $cur_time = time();                # set current time
 my $end_time = $cur_time - 86400;     # set end time to 24 hours ago
 my $start_time = $end_time - 2592000; # set start 30 days in the past
 # fetch average values from the 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");
 # save fetched values in a 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;
 # save the values from the 2-dimensional into a 1-dimensional array
 for $i ( 0 .. $#vals ) {
     $tot_mem[$count] = $vals[$i][1];
     $count++;
 }
 my $tot_mem_sum = 0;
 # calculate the total of all values
 for $i ( 0 .. ($count-1) ) {
     $tot_mem_sum = $tot_mem_sum + $tot_mem[$i];
 }
 # calculate the average of the array
 my $tot_mem_ave = $tot_mem_sum/($count);
 # create the graph
 RRDs::graph ("/images/mem_$count.png",   
             "--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:tot_mem_cor=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:tot_mem_cor#6699CC:Total memory consumed in MB");
 my $err=RRDs::error;
 if ($err) {print "problem generating the graph: $err\n";}
 # print 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.png.";


AUTHOR

Ketan Patel <k2pattu@yahoo.com>

rrdtool-1.4.7/doc/rrdcached.html0000644000175300017510000007556511707501544013535 00000000000000 rrdcached


NAME

rrdcached - Data caching daemon for rrdtool


SYNOPSIS

rrdcached [-P permissions] [-l address] [-s group] [-w timeout] [-z delay] [-f timeout] [-p pid_file] [-t write_threads] [-j journal_dir] [-F] [-g] [-b base_dir [-B]]


DESCRIPTION

rrdcached is a daemon that receives updates to existing RRD files, accumulates them and, if enough have been received or a defined time has passed, writes the updates to the RRD file. A flush command may be used to force writing of values to disk, so that graphing facilities and similar can work with up-to-date data.

The daemon was written with big setups in mind. Those setups usually run into IO related problems sooner or later for reasons that are beyond the scope of this document. Check the wiki at the RRDtool homepage for details. Also check SECURITY CONSIDERATIONS below before using this daemon! A detailed description of how the daemon operates can be found in the HOW IT WORKS section below.


OPTIONS

-l address

Tells the daemon to bind to address and accept incoming connections on that socket. If address begins with unix:, everything following that prefix is interpreted as the path to a UNIX domain socket. Otherwise the address or node name are resolved using getaddrinfo().

For network sockets, a port may be specified by using the form [address]:port. If the address is an IPv4 address or a fully qualified domain name (i. e. the address contains at least one dot (.)), the square brackets can be omitted, resulting in the (simpler) address:port pattern. The default port is 42217/tcp. If you specify a network socket, it is mandatory to read the SECURITY CONSIDERATIONS section.

The following formats are accepted. Please note that the address of the UNIX domain socket must start with a slash in the second case!

   unix:</path/to/unix.sock>
   /<path/to/unix.sock>
   <hostname-or-ip>
   [<hostname-or-ip>]:<port>
   <hostname-or-ipv4>:<port>

If the -l option is not specified the default address, unix:/tmp/rrdcached.sock, will be used.

-s group_name|gid

Set the group permissions of a UNIX domain socket. The option accepts either a numeric group id or group name. That group will then have both read and write permissions (the socket will have file permissions 0750) for the socket and, therefore, is able to send commands to the daemon. This may be useful in cases where you cannot easily run all RRD processes with the same user privileges (e.g. graph generating CGI scripts that typically run in the permission context of the web server).

This option affects the following UNIX socket addresses (the following -l options) or the default socket (if no -l options have been specified), i.e., you may specify different settings for different sockets.

The default is not to change ownership or permissions of the socket and, thus, use the system default.

-m mode

Set the file permissions of a UNIX domain socket. The option accepts an octal number representing the bit pattern for the mode (see chmod(1) for details).

Please note that not all systems honor this setting. On Linux, read/write permissions are required to connect to a UNIX socket. However, many BSD-derived systems ignore permissions for UNIX sockets. See unix(7) for details.

This option affects the following UNIX socket addresses (the following -l options) or the default socket (if no -l options have been specified), i.e., you may specify different settings for different sockets.

The default is not to change ownership or permissions of the socket and, thus, use the system default.

-P command[,command[,...]]

Specifies the commands accepted via a network socket. This allows administrators of RRDCacheD to control the actions accepted from various sources.

The arguments given to the -P option is a comma separated list of commands. For example, to allow the FLUSH and PENDING commands one could specify:

  rrdcached -P FLUSH,PENDING $MORE_ARGUMENTS

The -P option affects the following socket addresses (the following -l options) or the default socket (if no -l options have been specified). In the following example, only the IPv4 network socket (address 10.0.0.1) will be restricted to the FLUSH and PENDING commands:

  rrdcached -l unix:/some/path -P FLUSH,PENDING -l 10.0.0.1

A complete list of available commands can be found in the section Valid Commands below. There are two minor special exceptions:

  • The HELP and QUIT commands are always allowed.

  • If the BATCH command is accepted, the . command will automatically be accepted, too.

Please also read SECURITY CONSIDERATIONS below.

-w timeout

Data is written to disk every timeout seconds. If this option is not specified the default interval of 300 seconds will be used.

-z delay

If specified, rrdcached will delay writing of each RRD for a random number of seconds in the range [0,delay). This will avoid too many writes being queued simultaneously. This value should be no greater than the value specified in -w. By default, there is no delay.

-f timeout

Every timeout seconds the entire cache is searched for old values which are written to disk. This only concerns files to which updates have stopped, so setting this to a high value, such as 3600 seconds, is acceptable in most cases. This timeout defaults to 3600 seconds.

-p file

Sets the name and location of the PID-file. If not specified, the default, $localststedir/run/rrdcached.pid will be used.

-t write_threads

Specifies the number of threads used for writing RRD files. The default is 4. Increasing this number will allow rrdcached to have more simultaneous I/O requests into the kernel. This may allow the kernel to re-order disk writes, resulting in better disk throughput.

-j dir

Write updates to a journal in dir. In the event of a program or system crash, this will allow the daemon to write any updates that were pending at the time of the crash.

On startup, the daemon will check for journal files in this directory. If found, all updates therein will be read into memory before the daemon starts accepting new connections.

The journal will be rotated with the same frequency as the flush timer given by -f.

When journaling is enabled, the daemon will use a fast shutdown procedure. Rather than flushing all files to disk, it will make sure the journal is properly written and exit immediately. Although the RRD data files are not fully up-to-date, no information is lost; all pending updates will be replayed from the journal next time the daemon starts up.

To disable fast shutdown, use the -F option.

-F

ALWAYS flush all updates to the RRD data files when the daemon is shut down, regardless of journal setting.

-g

Run in the foreground. The daemon will not fork().

-b dir

The daemon will change into a specific directory at startup. All files passed to the daemon, that are specified by a relative path, will be interpreted to be relative to this directory. If not given the default, /tmp, will be used.

  +------------------------+------------------------+
  ! Command line           ! File updated           !
  +------------------------+------------------------+
  ! foo.rrd                ! /tmp/foo.rrd           !
  ! foo/bar.rrd            ! /tmp/foo/bar.rrd       !
  ! /var/lib/rrd/foo.rrd   ! /var/lib/rrd/foo.rrd   !
  +------------------------+------------------------+
  Paths given on the command  line and paths actually
  updated by the daemon,  assuming the base directory
  "/tmp".

WARNING: The paths up to and including the base directory MUST NOT BE symbolic links. In other words, if the base directory is specified as:

    -b /base/dir/somewhere

... then NONE of the following should be symbolic links:

    /base
    /base/dir
    /base/dir/somewhere
-B

Only permit writes into the base directory specified in -b (and any sub-directories). This does NOT detect symbolic links. Paths containing ../ will also be blocked.


AFFECTED RRDTOOL COMMANDS

The following commands may be made aware of the rrdcached using the command line argument --daemon or the environment variable RRDCACHED_ADDRESS:

  • dump

  • fetch

  • flush

  • graph

  • graphv

  • info

  • last

  • lastupdate

  • update

  • xport

The update command can send values to the daemon instead of writing them to the disk itself. All other commands can send a FLUSH command (see below) to the daemon before accessing the files, so they work with up-to-date data even if the cache timeout is large.


ERROR REPORTING

The daemon reports errors in one of two ways: During startup, error messages are printed to STDERR. One of the steps when starting up is to fork to the background and closing STDERR - after this writing directly to the user is no longer possible. Once this has happened, the daemon will send log messages to the system logging daemon using syslog(3). The facility used is LOG_DAEMON.


HOW IT WORKS

When receiving an update, rrdcached does not write to disk but looks for an entry for that file in its internal tree. If not found, an entry is created including the current time (called "First" in the diagram below). This time is not the time specified on the command line but the time the operating system considers to be "now". The value and time of the value (called "Time" in the diagram below) are appended to the tree node.

When appending a value to a tree node, it is checked whether it's time to write the values to disk. Values are written to disk if now() - First >= timeout, where timeout is the timeout specified using the -w option, see OPTIONS. If the values are "old enough" they will be enqueued in the "update queue", i. e. they will be appended to the linked list shown below. Because the tree nodes and the elements of the linked list are the same data structures in memory, any update to a file that has already been enqueued will be written with the next write to the RRD file, too.

A separate "update thread" constantly dequeues the first element in the update queue and writes all its values to the appropriate file. So as long as the update queue is not empty files are written at the highest possible rate.

Since the timeout of files is checked only when new values are added to the file, "dead" files, i. e. files that are not updated anymore, would never be written to disk. Therefore, every now and then, controlled by the -f option, the entire tree is walked and all "old" values are enqueued. Since this only affects "dead" files and walking the tree is relatively expensive, you should set the "flush interval" to a reasonably high value. The default is 3600 seconds (one hour).

The downside of caching values is that they won't show up in graphs generated from the RRD files. To get around this, the daemon provides the "flush command" to flush specific files. This means that the file is inserted at the head of the update queue or moved there if it is already enqueued. The flush command will return only after the file's pending updates have been written to disk.

 +------+   +------+                               +------+
 ! head !   ! root !                               ! tail !
 +---+--+   +---+--+                               +---+--+
     !         /\                                      !
     !        /  \                                     !
     !       /\  /\                                    !
     !      /\/\ \ `----------------- ... --------,    !
     V     /      `-------,                       !    V
 +---+----+---+    +------+-----+             +---+----+---+
 ! File:  foo !    ! File:  bar !             ! File:  qux !
 ! First: 101 !    ! First: 119 !             ! First: 180 !
 ! Next:&bar -+--->! Next:&... -+---> ... --->! Next:NULL  !
 | Prev:NULL  !<---+-Prev:&foo  !<--- ... ----+-Prev: &... !
 +============+    +============+             +============+
 ! Time:  100 !    ! Time:  120 !             ! Time:  180 !
 ! Value:  10 !    ! Value: 0.1 !             ! Value: 2,2 !
 +------------+    +------------+             +------------+
 ! Time:  110 !    ! Time:  130 !             ! Time:  190 !
 ! Value:  26 !    ! Value: 0.1 !             ! Value: 7,3 !
 +------------+    +------------+             +------------+
 :            :    :            :             :            :
 +------------+    +------------+             +------------+
 ! Time:  230 !    ! Time:  250 !             ! Time:  310 !
 ! Value:  42 !    ! Value: 0.2 !             ! Value: 1,2 !
 +------------+    +------------+             +------------+

The above diagram demonstrates:

  • Files/values are stored in a (balanced) tree.

  • Tree nodes and entries in the update queue are the same data structure.

  • The local time ("First") and the time specified in updates ("Time") may differ.

  • Timed out values are inserted at the "tail".

  • Explicitly flushed values are inserted at the "head".

  • ASCII art rocks.


SECURITY CONSIDERATIONS

Authentication

If your rrdtool installation was built without libwrap there is no form of authentication for clients connecting to the rrdcache daemon!

If your rrdtool installation was built with libwrap then you can use hosts_access to restrict client access to the rrdcache daemon (rrdcached). For more information on how to use hosts_access to restrict access to the rrdcache daemon you should read the hosts_access(5) man pages.

It is still highly recommended to install a packet filter or similar mechanism to prevent unauthorized connections. Unless you have a dedicated VLAN or VPN for this, using network sockets is probably a bad idea!

Authorization

There is minimal per-socket authorization.

Authorization is currently done on a per-socket basis. That means each socket has a list of commands it will accept and it will accept. It will accept only those commands explicitly listed but it will (currently) accept these commands from anyone reaching the socket.

If the networking sockets are to be used, it is necessary to restrict the accepted commands to those needed by external clients. If, for example, external clients want to draw graphs of the cached data, they should only be allowed to use the FLUSH command.

Encryption

There is no encryption.

Again, this may be added in the future, but for the time being it is your job to keep your private data private. Install a VPN or an encrypted tunnel if you statistics are confidential!

Sanity checking

There is no sanity checking.

The daemon will blindly write to any file it gets told, so you really should create a separate user just for this daemon. Also it does not do any sanity checks, so if it gets told to write values for a time far in the future, your files will be messed up good!

Conclusion

  • Security is the job of the administrator.

  • We recommend to allow write access via UNIX domain sockets only.

  • You have been warned.


PROTOCOL

The daemon communicates with clients using a line based ASCII protocol which is easy to read and easy to type. This makes it easy for scripts to implement the protocol and possible for users to use telnet to connect to the daemon and test stuff "by hand".

The protocol is line based, this means that each record consists of one or more lines. A line is terminated by the line feed character 0x0A, commonly written as \n. In the examples below, this character will be written as <LF> ("line feed").

After the connection has been established, the client is expected to send a "command". A command consists of the command keyword, possibly some arguments, and a terminating newline character. For a list of commands, see Valid Commands below.

Example:

  FLUSH /tmp/foo.rrd<LF>

The daemon answers with a line consisting of a status code and a short status message, separated by one or more space characters. A negative status code signals an error, a positive status code or zero signal success. If the status code is greater than zero, it indicates the number of lines that follow the status line.

Examples:

 0 Success<LF>
 2 Two lines follow<LF>
 This is the first line<LF>
 And this is the second line<LF>

Valid Commands

The following commands are understood by the daemon:

FLUSH filename

Causes the daemon to put filename to the head of the update queue (possibly moving it there if the node is already enqueued). The answer will be sent after the node has been dequeued.

FLUSHALL

Causes the daemon to start flushing ALL pending values to disk. This returns immediately, even though the writes may take a long time.

PENDING filename

Shows any "pending" updates for a file, in order. The updates shown have not yet been written to the underlying RRD file.

FORGET filename

Removes filename from the cache. Any pending updates WILL BE LOST.

QUEUE

Shows the files that are on the output queue. Returns zero or more lines in the following format, where <num_vals> is the number of values to be written for the <file>:

    <num_vals> <file>
HELP [command]

Returns a short usage message. If no command is given, or command is HELP, a list of commands supported by the daemon is returned. Otherwise a short description, possibly containing a pointer to a manual page, is returned. Obviously, this is meant for interactive usage and the format in which the commands and usage summaries are returned is not well defined.

STATS

Returns a list of metrics which can be used to measure the daemons performance and check its status. For a description of the values returned, see Performance Values below.

The format in which the values are returned is similar to many other line based protocols: Each value is printed on a separate line, each consisting of the name of the value, a colon, one or more spaces and the actual value.

Example:

 9 Statistics follow
 QueueLength: 0
 UpdatesReceived: 30
 FlushesReceived: 2
 UpdatesWritten: 13
 DataSetsWritten: 390
 TreeNodesNumber: 13
 TreeDepth: 4
 JournalBytes: 190
 JournalRotate: 0
UPDATE filename values [values ...]

Adds more data to a filename. This is the operation the daemon was designed for, so describing the mechanism again is unnecessary. Read HOW IT WORKS above for a detailed explanation.

Note that rrdcached only accepts absolute timestamps in the update values. Updates strings like "N:1:2:3" are automatically converted to absolute time by the RRD client library before sending to rrdcached.

WROTE filename

This command is written to the journal after a file is successfully written out to disk. It is used during journal replay to determine which updates have already been applied. It is only valid in the journal; it is not accepted from the other command channels.

BATCH

This command initiates the bulk load of multiple commands. This is designed for installations with extremely high update rates, since it permits more than one command to be issued per read() and write().

All commands are executed just as they would be if given individually, except for output to the user. Messages indicating success are suppressed, and error messages are delayed until the client is finished.

Command processing is finished when the client sends a dot (".") on its own line. After the client has finished, the server responds with an error count and the list of error messages (if any). Each error messages indicates the number of the command to which it corresponds, and the error message itself. The first user command after BATCH is command number one.

    client:  BATCH
    server:  0 Go ahead.  End with dot '.' on its own line.
    client:  UPDATE x.rrd 1223661439:1:2:3            <--- command #1
    client:  UPDATE y.rrd 1223661440:3:4:5            <--- command #2
    client:  and so on...
    client:  .
    server:  2 Errors
    server:  1 message for command 1
    server:  12 message for command 12
QUIT

Disconnect from rrdcached.

Performance Values

The following counters are returned by the STATS command:

QueueLength (unsigned 64bit integer)

Number of nodes currently enqueued in the update queue.

UpdatesReceived (unsigned 64bit integer)

Number of UPDATE commands received.

FlushesReceived (unsigned 64bit integer)

Number of FLUSH commands received.

UpdatesWritten (unsigned 64bit integer)

Total number of updates, i. e. calls to rrd_update_r, since the daemon was started.

DataSetsWritten (unsigned 64bit integer)

Total number of "data sets" written to disk since the daemon was started. A data set is one or more values passed to the UPDATE command. For example: 1223661439:123:456 is one data set with two values. The term "data set" is used to prevent confusion whether individual values or groups of values are counted.

TreeNodesNumber (unsigned 64bit integer)

Number of nodes in the cache.

TreeDepth (unsigned 64bit integer)

Depth of the tree used for fast key lookup.

JournalBytes (unsigned 64bit integer)

Total number of bytes written to the journal since startup.

JournalRotate (unsigned 64bit integer)

Number of times the journal has been rotated since startup.


SIGNALS

SIGINT and SIGTERM

The daemon exits normally on receipt of either of these signals. Pending updates are handled in accordance with the -j and -F options.

SIGUSR1

The daemon exits AFTER flushing all updates out to disk. This may take a while.

SIGUSR2

The daemon exits immediately, without flushing updates out to disk. Pending updates will be replayed from the journal when the daemon starts up again. WARNING: if journaling (-j) is NOT enabled, any pending updates WILL BE LOST.


BUGS

No known bugs at the moment.


SEE ALSO

the rrdtool manpage, the rrdgraph manpage


AUTHOR

Florian Forster <octo at verplant.org>

Both rrdcached and this manual page have been written by Florian.


CONTRIBUTORS

kevin brintnall <kbrint@rufus.net>

rrdtool-1.4.7/doc/rrdtool.html0000644000175300017510000004126311707501544013267 00000000000000 rrdtool

NAME

rrdtool - Round Robin Database Tool


SYNOPSIS

rrdtool - [workdir]| function


DESCRIPTION

OVERVIEW

It is pretty easy to gather status information from all sorts of things, ranging from the temperature in your office to the number of octets which have passed through the FDDI interface of your router. But it is not so trivial to store this data in an efficient and systematic manner. This is where RRDtool comes in handy. It lets you log and analyze the data you gather from all kinds of data-sources (DS). The data analysis part of RRDtool is based on the ability to quickly generate graphical representations of the data values collected over a definable time period.

In this man page you will find general information on the design and functionality of the Round Robin Database Tool (RRDtool). For a more detailed description of how to use the individual functions of RRDtool check the corresponding man page.

For an introduction to the usage of RRDtool make sure you consult the the rrdtutorial manpage.

FUNCTIONS

While the man pages talk of command line switches you have to set in order to make RRDtool work it is important to note that RRDtool can be remotely controlled through a set of pipes. This saves a considerable amount of startup time when you plan to make RRDtool do a lot of things quickly. Check the section on Remote_Control further down. There is also a number of language bindings for RRDtool which allow you to use it directly from Perl, python, Tcl, PHP, etc.

create

Set up a new Round Robin Database (RRD). Check the rrdcreate manpage.

update

Store new data values into an RRD. Check the rrdupdate manpage.

updatev

Operationally equivalent to update except for output. Check the rrdupdate manpage.

graph

Create a graph from data stored in one or several RRDs. Apart from generating graphs, data can also be extracted to stdout. Check the rrdgraph manpage.

dump

Dump the contents of an RRD in plain ASCII. In connection with restore you can use this to move an RRD from one computer architecture to another. Check the rrddump manpage.

restore

Restore an RRD in XML format to a binary RRD. Check the rrdrestore manpage

fetch

Get data for a certain time period from a RRD. The graph function uses fetch to retrieve its data from an RRD. Check the rrdfetch manpage.

tune

Alter setup of an RRD. Check the rrdtune manpage.

last

Find the last update time of an RRD. Check the rrdlast manpage.

info

Get information about an RRD. Check the rrdinfo manpage.

rrdresize

Change the size of individual RRAs. This is dangerous! Check the rrdresize manpage.

xport

Export data retrieved from one or several RRDs. Check the rrdxport manpage.

flushcached

Flush the values for a specific RRD file from memory. Check the rrdflushcached manpage.

rrdcgi

This is a standalone tool for producing RRD graphs on the fly. Check the rrdcgi manpage.

HOW DOES RRDTOOL WORK?

Data Acquisition

When monitoring the state of a system, it is convenient to have the data available at a constant time interval. Unfortunately, you may not always be able to fetch data at exactly the time you want to. Therefore RRDtool lets you update the log file at any time you want. It will automatically interpolate the value of the data-source (DS) at the latest official time-slot (interval) and write this interpolated value to the log. The original value you have supplied is stored as well and is also taken into account when interpolating the next log entry.

Consolidation

You may log data at a 1 minute interval, but you might also be interested to know the development of the data over the last year. You could do this by simply storing the data in 1 minute intervals for the whole year. While this would take considerable disk space it would also take a lot of time to analyze the data when you wanted to create a graph covering the whole year. RRDtool offers a solution to this problem through its data consolidation feature. When setting up an Round Robin Database (RRD), you can define at which interval this consolidation should occur, and what consolidation function (CF) (average, minimum, maximum, total, last) should be used to build the consolidated values (see rrdcreate). You can define any number of different consolidation setups within one RRD. They will all be maintained on the fly when new data is loaded into the RRD.

Round Robin Archives

Data values of the same consolidation setup are stored into Round Robin Archives (RRA). This is a very efficient manner to store data for a certain amount of time, while using a known and constant amount of storage space.

It works like this: If you want to store 1'000 values in 5 minute interval, RRDtool will allocate space for 1'000 data values and a header area. In the header it will store a pointer telling which slots (value) in the storage area was last written to. New values are written to the Round Robin Archive in, you guessed it, a round robin manner. This automatically limits the history to the last 1'000 values (in our example). Because you can define several RRAs within a single RRD, you can setup another one, for storing 750 data values at a 2 hour interval, for example, and thus keep a log for the last two months at a lower resolution.

The use of RRAs guarantees that the RRD does not grow over time and that old data is automatically eliminated. By using the consolidation feature, you can still keep data for a very long time, while gradually reducing the resolution of the data along the time axis.

Using different consolidation functions (CF) allows you to store exactly the type of information that actually interests you: the maximum one minute traffic on the LAN, the minimum temperature of your wine cellar, the total minutes of down time, etc.

Unknown Data

As mentioned earlier, the RRD stores data at a constant interval. Sometimes it may happen that no new data is available when a value has to be written to the RRD. Data acquisition may not be possible for one reason or other. With RRDtool you can handle these situations by storing an *UNKNOWN* value into the database. The value '*UNKNOWN*' is supported through all the functions of the tool. When consolidating a data set, the amount of *UNKNOWN* data values is accounted for and when a new consolidated value is ready to be written to its Round Robin Archive (RRA), a validity check is performed to make sure that the percentage of unknown values in the data point is above a configurable level. If not, an *UNKNOWN* value will be written to the RRA.

Graphing

RRDtool allows you to generate reports in numerical and graphical form based on the data stored in one or several RRDs. The graphing feature is fully configurable. Size, color and contents of the graph can be defined freely. Check the rrdgraph manpage for more information on this.

Aberrant Behavior Detection

by Jake Brutlag

RRDtool provides the building blocks for near real-time aberrant behavior detection. These components include:

  • An algorithm for predicting the value of a time series one time step into the future.

  • A measure of deviation between predicted and observed values.

  • A mechanism to decide if and when an observed value or sequence of observed values is too deviant from the predicted value(s).

Here is a brief explanation of these components:

The Holt-Winters time series forecasting algorithm is an on-line (or incremental) algorithm that adaptively predicts future observations in a time series. Its forecast is the sum of three components: a baseline (or intercept), a linear trend over time (or slope), and a seasonal coefficient (a periodic effect, such as a daily cycle). There is one seasonal coefficient for each time point in the period (cycle). After a value is observed, each of these components is updated via exponential smoothing. This means that the algorithm "learns" from past values and uses them to predict the future. The rate of adaptation is governed by 3 parameters, alpha (intercept), beta (slope), and gamma (seasonal). The prediction can also be viewed as a smoothed value for the time series.

The measure of deviation is a seasonal weighted absolute deviation. The term seasonal means deviation is measured separately for each time point in the seasonal cycle. As with Holt-Winters forecasting, deviation is predicted using the measure computed from past values (but only at that point in the seasonal cycle). After the value is observed, the algorithm learns from the observed value via exponential smoothing. Confidence bands for the observed time series are generated by scaling the sequence of predicted deviation values (we usually think of the sequence as a continuous line rather than a set of discrete points).

Aberrant behavior (a potential failure) is reported whenever the number of times the observed value violates the confidence bands meets or exceeds a specified threshold within a specified temporal window (e.g. 5 violations during the past 45 minutes with a value observed every 5 minutes).

This functionality is embedded in a set of related RRAs. In particular, a FAILURES RRA logs potential failures. With these data you could, for example, use a front-end application to RRDtool to initiate real-time alerts.

For a detailed description on how to set this up, see the rrdcreate manpage.

REMOTE CONTROL

When you start RRDtool with the command line option '-' it waits for input via standard input (STDIN). With this feature you can improve performance by attaching RRDtool to another process (MRTG is one example) through a set of pipes. Over these pipes RRDtool accepts the same arguments as on the command line and some special commands like quit, cd, mkdir and ls. For detailed help on the server commands type:

   rrdtool help cd|mkdir|pwd|ls|quit

When a command is completed, RRDtool will print the string 'OK', followed by timing information of the form u:usertime s:systemtime. Both values are the running totals of seconds since RRDtool was started. If an error occurs, a line of the form 'ERROR: Description of error' will be printed instead. RRDtool will not abort, unless something really serious happens. If a workdir is specified and the UID is 0, RRDtool will do a chroot to that workdir. If the UID is not 0, RRDtool only changes the current directory to workdir.

RRD Server

If you want to create a RRD-Server, you must choose a TCP/IP Service number and add them to /etc/services like this:

 rrdsrv      13900/tcp                       # RRD server

Attention: the TCP port 13900 isn't officially registered for rrdsrv. You can use any unused port in your services file, but the server and the client system must use the same port, of course.

With this configuration you can add RRDtool as meta-server to /etc/inetd.conf. For example:

 rrdsrv stream tcp nowait root /opt/rrd/bin/rrdtool rrdtool - /var/rrd

Don't forget to create the database directory /var/rrd and reinitialize your inetd.

If all was setup correctly, you can access the server with Perl sockets, tools like netcat, or in a quick interactive test by using 'telnet localhost rrdsrv'.

NOTE: that there is no authentication with this feature! Do not setup such a port unless you are sure what you are doing.


RRDCACHED, THE CACHING DAEMON

For very big setups, updating thousands of RRD files often becomes a serious IO problem. If you run into such problems, you might want to take a look at the rrdcached manpage, a caching daemon for RRDtool which may help you lessen the stress on your disks.


SEE ALSO

rrdcreate, rrdupdate, rrdgraph, rrddump, rrdfetch, rrdtune, rrdlast, rrdxport, rrdflushcached, rrdcached


BUGS

Bugs? Features!


AUTHOR

Tobias Oetiker <tobi@oetiker.ch>

rrdtool-1.4.7/doc/rrdgraph_rpn.html0000644000175300017510000004341511707501544014273 00000000000000 rrdgraph_rpn

NAME

rrdgraph_rpn - About RPN Math in rrdtool graph


SYNOPSIS

RPN expression:=vname|operator|value[,RPN expression]


DESCRIPTION

If you have ever used a traditional HP calculator you already know RPN (Reverse Polish Notation). The idea behind RPN is that you have a stack and push your data onto this stack. Whenever you execute an operation, it takes as many elements from the stack as needed. Pushing is done implicitly, so whenever you specify a number or a variable, it gets pushed onto the stack automatically.

At the end of the calculation there should be one and only one value left on the stack. This is the outcome of the function and this is what is put into the vname. For CDEF instructions, the stack is processed for each data point on the graph. VDEF instructions work on an entire data set in one run. Note, that currently VDEF instructions only support a limited list of functions.

Example: VDEF:maximum=mydata,MAXIMUM

This will set variable "maximum" which you now can use in the rest of your RRD script.

Example: CDEF:mydatabits=mydata,8,*

This means: push variable mydata, push the number 8, execute the operator *. The operator needs two elements and uses those to return one value. This value is then stored in mydatabits. As you may have guessed, this instruction means nothing more than mydatabits = mydata * 8. The real power of RPN lies in the fact that it is always clear in which order to process the input. For expressions like a = b + 3 * 5 you need to multiply 3 with 5 first before you add b to get a. However, with parentheses you could change this order: a = (b + 3) * 5. In RPN, you would do a = b, 3, +, 5, * without the need for parentheses.


OPERATORS

Boolean operators

LT, LE, GT, GE, EQ, NE

Pop two elements from the stack, compare them for the selected condition and return 1 for true or 0 for false. Comparing an unknown or an infinite value will result in unknown returned ... which will also be treated as false by the IF call.

UN, ISINF

Pop one element from the stack, compare this to unknown respectively to positive or negative infinity. Returns 1 for true or 0 for false.

IF

Pops three elements from the stack. If the element popped last is 0 (false), the value popped first is pushed back onto the stack, otherwise the value popped second is pushed back. This does, indeed, mean that any value other than 0 is considered to be true.

Example: A,B,C,IF should be read as if (A) then (B) else (C)

Comparing values

MIN, MAX

Pops two elements from the stack and returns the smaller or larger, respectively. Note that infinite is larger than anything else. If one of the input numbers is unknown then the result of the operation will be unknown too.

LIMIT

Pops two elements from the stack and uses them to define a range. Then it pops another element and if it falls inside the range, it is pushed back. If not, an unknown is pushed.

The range defined includes the two boundaries (so: a number equal to one of the boundaries will be pushed back). If any of the three numbers involved is either unknown or infinite this function will always return an unknown

Example: CDEF:a=alpha,0,100,LIMIT will return unknown if alpha is lower than 0 or if it is higher than 100.

Arithmetics

+, -, *, /, %

Add, subtract, multiply, divide, modulo

ADDNAN

NAN-safe addition. If one parameter is NAN/UNKNOWN it'll be treated as zero. If both parameters are NAN/UNKNOWN, NAN/UNKNOWN will be returned.

SIN, COS, LOG, EXP, SQRT

Sine and cosine (input in radians), log and exp (natural logarithm), square root.

ATAN

Arctangent (output in radians).

ATAN2

Arctangent of y,x components (output in radians). This pops one element from the stack, the x (cosine) component, and then a second, which is the y (sine) component. It then pushes the arctangent of their ratio, resolving the ambiguity between quadrants.

Example: CDEF:angle=Y,X,ATAN2,RAD2DEG will convert X,Y components into an angle in degrees.

FLOOR, CEIL

Round down or up to the nearest integer.

DEG2RAD, RAD2DEG

Convert angle in degrees to radians, or radians to degrees.

ABS

Take the absolute value.

Set Operations

SORT, REV

Pop one element from the stack. This is the count of items to be sorted (or reversed). The top count of the remaining elements are then sorted (or reversed) in place on the stack.

Example: CDEF:x=v1,v2,v3,v4,v5,v6,6,SORT,POP,5,REV,POP,+,+,+,4,/ will compute the average of the values v1 to v6 after removing the smallest and largest.

AVG

Pop one element (count) from the stack. Now pop count elements and build the average, ignoring all UNKNOWN values in the process.

Example: CDEF:x=a,b,c,d,4,AVG

TREND, TRENDNAN

Create a "sliding window" average of another data series.

Usage: CDEF:smoothed=x,1800,TREND

This will create a half-hour (1800 second) sliding window average of x. The average is essentially computed as shown here:

                 +---!---!---!---!---!---!---!---!--->
                                                     now
                       delay     t0
                 <--------------->
                         delay       t1
                     <--------------->
                              delay      t2
                         <--------------->
     Value at sample (t0) will be the average between (t0-delay) and (t0)
     Value at sample (t1) will be the average between (t1-delay) and (t1)
     Value at sample (t2) will be the average between (t2-delay) and (t2)

TRENDNAN is - in contrast to TREND - NAN-safe. If you use TREND and one source value is NAN the complete sliding window is affected. The TRENDNAN operation ignores all NAN-values in a sliding window and computes the average of the remaining values.

PREDICT, PREDICTSIGMA

Create a "sliding window" average/sigma of another data series, that also shifts the data series by given amounts of of time as well

Usage - explicit stating shifts: CDEF:predict=<shift n>,...,<shift 1>,n,<window>,x,PREDICT CDEF:sigma=<shift n>,...,<shift 1>,n,<window>,x,PREDICTSIGMA

Usage - shifts defined as a base shift and a number of time this is applied CDEF:predict=<shift multiplier>,-n,<window>,x,PREDICT CDEF:sigma=<shift multiplier>,-n,<window>,x,PREDICTSIGMA

Example: CDEF:predict=172800,86400,2,1800,x,PREDICT

This will create a half-hour (1800 second) sliding window average/sigma of x, that average is essentially computed as shown here:

 +---!---!---!---!---!---!---!---!---!---!---!---!---!---!---!---!---!--->
                                                                     now
                                                  shift 1        t0
                                         <----------------------->
                               window
                         <--------------->
                                       shift 2
                 <----------------------------------------------->
       window
 <--------------->
                                                      shift 1        t1
                                             <----------------------->
                                   window
                             <--------------->
                                            shift 2
                     <----------------------------------------------->
           window
     <--------------->
 Value at sample (t0) will be the average between (t0-shift1-window) and (t0-shift1)
                                      and between (t0-shift2-window) and (t0-shift2)
 Value at sample (t1) will be the average between (t1-shift1-window) and (t1-shift1)
                                      and between (t1-shift2-window) and (t1-shift2)

The function is by design NAN-safe. This also allows for extrapolation into the future (say a few days) - you may need to define the data series whit the optional start= parameter, so that the source data series has enough data to provide prediction also at the beginning of a graph...

Here an example, that will create a 10 day graph that also shows the prediction 3 days into the future with its uncertainty value (as defined by avg+-4*sigma) This also shows if the prediction is exceeded at a certain point.

rrdtool graph image.png --imgformat=PNG \ --start=-7days --end=+3days --width=1000 --height=200 --alt-autoscale-max \ DEF:value=value.rrd:value:AVERAGE:start=-14days \ LINE1:value#ff0000:value \ CDEF:predict=86400,-7,1800,value,PREDICT \ CDEF:sigma=86400,-7,1800,value,PREDICTSIGMA \ CDEF:upper=predict,sigma,3,*,+ \ CDEF:lower=predict,sigma,3,*,- \ LINE1:predict#00ff00:prediction \ LINE1:upper#0000ff:upper\ certainty\ limit \ LINE1:lower#0000ff:lower\ certainty\ limit \ CDEF:exceeds=value,UN,0,value,lower,upper,LIMIT,UN,IF \ TICK:exceeds#aa000080:1

Note: Experience has shown that a factor between 3 and 5 to scale sigma is a good discriminator to detect abnormal behavior. This obviously depends also on the type of data and how "noisy" the data series is.

This prediction can only be used for short term extrapolations - say a few days into the future-

Special values

UNKN

Pushes an unknown value on the stack

INF, NEGINF

Pushes a positive or negative infinite value on the stack. When such a value is graphed, it appears at the top or bottom of the graph, no matter what the actual value on the y-axis is.

PREV

Pushes an unknown value if this is the first value of a data set or otherwise the result of this CDEF at the previous time step. This allows you to do calculations across the data. This function cannot be used in VDEF instructions.

PREV(vname)

Pushes an unknown value if this is the first value of a data set or otherwise the result of the vname variable at the previous time step. This allows you to do calculations across the data. This function cannot be used in VDEF instructions.

COUNT

Pushes the number 1 if this is the first value of the data set, the number 2 if it is the second, and so on. This special value allows you to make calculations based on the position of the value within the data set. This function cannot be used in VDEF instructions.

Time

Time inside RRDtool is measured in seconds since the epoch. The epoch is defined to be Thu Jan  1 00:00:00 UTC 1970.

NOW

Pushes the current time on the stack.

TIME

Pushes the time the currently processed value was taken at onto the stack.

LTIME

Takes the time as defined by TIME, applies the time zone offset valid at that time including daylight saving time if your OS supports it, and pushes the result on the stack. There is an elaborate example in the examples section below on how to use this.

Processing the stack directly

DUP, POP, EXC

Duplicate the top element, remove the top element, exchange the two top elements.


VARIABLES

These operators work only on VDEF statements. Note that currently ONLY these work for VDEF.

MAXIMUM, MINIMUM, AVERAGE

Return the corresponding value, MAXIMUM and MINIMUM also return the first occurrence of that value in the time component.

Example: VDEF:avg=mydata,AVERAGE

STDEV

Returns the standard deviation of the values.

Example: VDEF:stdev=mydata,STDEV

LAST, FIRST

Return the last/first non-nan or infinite value for the selected data stream, including its timestamp.

Example: VDEF:first=mydata,FIRST

TOTAL

Returns the rate from each defined time slot multiplied with the step size. This can, for instance, return total bytes transferred when you have logged bytes per second. The time component returns the number of seconds.

Example: VDEF:total=mydata,TOTAL

PERCENT, PERCENTNAN

This should follow a DEF or CDEF vname. The vname is popped, another number is popped which is a certain percentage (0..100). The data set is then sorted and the value returned is chosen such that percentage percent of the values is lower or equal than the result. For PERCENTNAN Unknown values are ignored, but for PERCENT Unknown values are considered lower than any finite number for this purpose so if this operator returns an unknown you have quite a lot of them in your data. Infinite numbers are lesser, or more, than the finite numbers and are always more than the Unknown numbers. (NaN < -INF < finite values < INF)

Example: VDEF:perc95=mydata,95,PERCENT VDEF:percnan95=mydata,95,PERCENTNAN

LSLSLOPE, LSLINT, LSLCORREL

Return the parameters for a Least Squares Line (y = mx +b) which approximate the provided dataset. LSLSLOPE is the slope (m) of the line related to the COUNT position of the data. LSLINT is the y-intercept (b), which happens also to be the first data point on the graph. LSLCORREL is the Correlation Coefficient (also know as Pearson's Product Moment Correlation Coefficient). It will range from 0 to +/-1 and represents the quality of fit for the approximation.

Example: VDEF:slope=mydata,LSLSLOPE


SEE ALSO

the rrdgraph manpage gives an overview of how rrdtool graph works. the rrdgraph_data manpage describes DEF,CDEF and VDEF in detail. the rrdgraph_rpn manpage describes the RPN language used in the ?DEF statements. the rrdgraph_graph manpage page describes all of the graph and print functions.

Make sure to read the rrdgraph_examples manpage for tips&tricks.


AUTHOR

Program by Tobias Oetiker <tobi@oetiker.ch>

This manual page by Alex van den Bogaerdt <alex@vandenbogaerdt.nl> with corrections and/or additions by several people

rrdtool-1.4.7/doc/rrdfirst.html0000644000175300017510000000343211707501544013435 00000000000000 rrdfirst

NAME

rrdfirst - Return the date of the first data sample in an RRA within an RRD


SYNOPSIS

rrdtool first filename [--rraindex number]


DESCRIPTION

The first function returns the UNIX timestamp of the first data sample entered into the specified RRA of the RRD file.

filename

The name of the RRD that contains the data.

--rraindex number

The index number of the RRA that is to be examined. If not specified, the index defaults to zero. RRA index numbers can be determined through rrdtool info.


AUTHOR

Burton Strauss <Burton@ntopSupport.com>

rrdtool-1.4.7/doc/rpntutorial.html0000644000175300017510000002121411707501544014157 00000000000000 rpntutorial

NAME

rpntutorial - Reading RRDtool RPN Expressions by Steve Rader


DESCRIPTION

This tutorial should help you get to grips with RRDtool RPN expressions as seen in CDEF arguments of RRDtool graph.


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, then 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 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.


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 bizarre 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.


Some Examples

While compound expressions can look overly complex, they can be considered elegantly simple. To quickly comprehend RPN expressions, you must know 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 and is replaced by 5. This results in the stack "1,5,+". Finally, "1,5,+" is evaluated resulting in the answer 6. For convenience, 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 conveniently 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,              7000,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  ( lets call this 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 with it's verbose description again and--voila!--you have an easily readable description of the expression:

 if input > 10 then 10 else input

Finally, let's go back to 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)


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 traditional 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.  Parentheses 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 was 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.)


AUTHOR

Steve Rader <rader@wiscnet.net>

rrdtool-1.4.7/doc/rrdxport.html0000644000175300017510000002253511707501544013467 00000000000000 rrdxport

NAME

rrdxport - Export data in XML format based on data from one or several RRD


SYNOPSIS

rrdtool xport [-s|--start seconds] [-e|--end seconds] [-m|--maxrows rows] [--step value] [--json] [--daemon address] [DEF:vname=rrd:ds-name:CF] [CDEF:vname=rpn-expression] [XPORT:vname[:legend]]


DESCRIPTION

The xport function's main purpose is to write an XML formatted representation of the data stored in one or several RRDs. It can also extract numerical reports.

If no XPORT statements are found, there will be no output.

-s|--start seconds (default end-1day)

The time when the exported range should begin. 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 printed. See also AT-STYLE TIME SPECIFICATION section in the rrdfetch documentation for a detailed explanation on how to specify time.

-e|--end seconds (default now)

The time when the exported range should end. Time in seconds since epoch. See also AT-STYLE TIME SPECIFICATION section in the rrdfetch documentation for a detailed explanation of ways to specify time.

-m|--maxrows rows (default 400 rows)

This works like the -w|--width parameter of rrdgraph. In fact it is exactly the same, but the parameter was renamed to describe its purpose in this module. See rrdgraph documentation for details.

--step value (default automatic)

See the rrdgraph manpage documentation.

--daemon address

Address of the the rrdcached manpage daemon. If specified, a flush command is sent to the server before reading the RRD files. This allows rrdtool to return fresh data even if the daemon is configured to cache values for a long time. For a list of accepted formats, see the -l option in the the rrdcached manpage manual.

  rrdtool xport --daemon unix:/var/run/rrdcached.sock ...
--json

produce json formated output (instead of xml)

--enumds

The generated xml should contain the data values in enumerated tags.

 <v0>val</v0><v1>val</v1>
DEF:vname=rrd:ds-name:CF

See rrdgraph documentation.

CDEF:vname=rpn-expression

See rrdgraph documentation.

XPORT:vname::legend

At least one XPORT statement should be present. The values referenced by vname are printed. Optionally add a legend.


Output format

The output is enclosed in an xport element and contains two blocks. The first block is enclosed by a meta element and contains some meta data. The second block is enclosed by a data element and contains the data rows.

Let's assume that the xport command looks like this:

  rrdtool xport \
          --start now-1h --end now \
          DEF:xx=host-inout.lo.rrd:output:AVERAGE \
          DEF:yy=host-inout.lo.rrd:input:AVERAGE \
          CDEF:aa=xx,yy,+,8,* \
          XPORT:xx:"out bytes" \
          XPORT:aa:"in and out bits"

The resulting meta data section is (the values will depend on the RRD characteristics):

  <meta>
    <start>1020611700</start>
    <step>300</step>
    <end>1020615600</end>
    <rows>14</rows>
    <columns>2</columns>
    <legend>
      <entry>out bytes</entry>
      <entry>in and out bits</entry>
    </legend>
  </meta>

The resulting data section is:

  <data>
    <row><t>1020611700</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
    <row><t>1020612000</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
    <row><t>1020612300</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
    <row><t>1020612600</t><v>3.4113333333e+00</v><v>5.4581333333e+01</v></row>
    <row><t>1020612900</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
    <row><t>1020613200</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
    <row><t>1020613500</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
    <row><t>1020613800</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
    <row><t>1020614100</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
    <row><t>1020614400</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
    <row><t>1020614700</t><v>3.7333333333e+00</v><v>5.9733333333e+01</v></row>
    <row><t>1020615000</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
    <row><t>1020615300</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
    <row><t>1020615600</t><v>NaN</v><v>NaN</v></row>
  </data>


EXAMPLE 1

  rrdtool xport \
          DEF:out=if1-inouts.rrd:outoctets:AVERAGE \
          XPORT:out:"out bytes"


EXAMPLE 2

  rrdtool xport \
          DEF:out1=if1-inouts.rrd:outoctets:AVERAGE \
          DEF:out2=if2-inouts.rrd:outoctets:AVERAGE \
          CDEF:sum=out1,out2,+ \
          XPORT:out1:"if1 out bytes" \
          XPORT:out2:"if2 out bytes" \
          XPORT:sum:"output sum"


ENVIRONMENT VARIABLES

The following environment variables may be used to change the behavior of rrdtoolxport:

RRDCACHED_ADDRESS

If this environment variable is set it will have the same effect as specifying the --daemon option on the command line. If both are present, the command line argument takes precedence.


AUTHOR

Tobias Oetiker <tobi@oetiker.ch>

rrdtool-1.4.7/doc/rrdthreads.html0000644000175300017510000001305611707501544013743 00000000000000 rrdthreads

NAME

rrdthreads - Provisions for linking the RRD library to use in multi-threaded programs


SYNOPSIS

Using librrd in multi-threaded programs requires some extra precautions, as the RRD library in its original form was not thread-safe at all. This document describes requirements and pitfalls on the way to use the multi-threaded version of librrd in your own programs. It also gives hints for future RRD development to keep the library thread-safe.

Currently only some RRD operations are implemented in a thread-safe way. They all end in the usual "_r" suffix.


DESCRIPTION

In order to use librrd in multi-threaded programs you must:

  • Link with librrd_th instead of librrd (use -lrrd_th when linking)

  • Use the "_r" functions instead of the normal API-functions

  • Do not use any at-style time specifications. Parsing of such time specifications is terribly non-thread-safe.

  • Never use non *_r functions unless it is explicitly documented that the function is tread-safe.

  • Every thread SHOULD call rrd_get_context() before its first call to any librrd_th function in order to set up thread specific data. This is not strictly required, but it is the only way to test if memory allocation can be done by this function. Otherwise the program may die with a SIGSEGV in a low-memory situation.

  • Always call rrd_error_clear() before any call to the library. Otherwise the call might fail due to some earlier error.

NOTES FOR RRD CONTRIBUTORS

Some precautions must be followed when developing RRD from now on:

  • Only use thread-safe functions in library code. Many often used libc functions aren't thread-safe. Take care in the following situations or when using the following library functions:

    • Direct calls to strerror() must be avoided: use rrd_strerror() instead, it provides a per-thread error message.

    • The getpw*, getgr*, gethost* function families (and some more get* functions) are not thread-safe: use the *_r variants

    • Time functions: asctime, ctime, gmtime, localtime: use *_r variants

    • strtok: use strtok_r

    • tmpnam: use tmpnam_r

    • Many others (lookup documentation)

  • A header file named rrd_is_thread_safe.h is provided that works with the GNU C-preprocessor to "poison" some of the most common non-thread-safe functions using the #pragma GCC poison directive. Just include this header in source files you want to keep thread-safe.

  • Do not introduce global variables!

    If you really, really have to use a global variable you may add a new field to the rrd_context structure and modify rrd_error.c, rrd_thread_safe.c and rrd_non_thread_safe.c

  • Do not use getopt or getopt_long in *_r (neither directly nor indirectly).

    getopt uses global variables and behaves badly in a multi-threaded application when called concurrently. Instead provide a *_r function taking all options as function parameters. You may provide argc and **argv arguments for variable length argument lists. See rrd_update_r as an example.

  • Do not use the rrd_parsetime function!

    It uses lots of global variables. You may use it in functions not designed to be thread-safe, like in functions wrapping the _r version of some operation (e.g., rrd_create, but not in rrd_create_r)

CURRENTLY IMPLEMENTED THREAD SAFE FUNCTIONS

Currently there exist thread-safe variants of rrd_update, rrd_create, rrd_dump, rrd_info, rrd_last, and rrd_fetch.


AUTHOR

Peter Stamfest <peter@stamfest.at>

rrdtool-1.4.7/doc/rrdgraph_graph.html0000644000175300017510000007250711707501544014601 00000000000000 rrdgraph_graph


NAME

rrdgraph_graph - rrdtool graph command reference


SYNOPSIS

PRINT:vname:format

GPRINT:vname:format

COMMENT:text

VRULE:time#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]]

HRULE:value#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]]

LINE[width]:value[#color][:[legend][:STACK]][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]]

AREA:value[#color][:[legend][:STACK]]

TICK:vname#rrggbb[aa][:fraction[:legend]]

SHIFT:vname:offset

TEXTALIGN:{left|right|justified|center}

PRINT:vname:CF:format (deprecated)

GPRINT:vname:CF:format (deprecated)

STACK:vname#color[:legend] (deprecated)


DESCRIPTION

These instructions allow you to generate your image or report. If you don't use any graph elements, no graph is generated. Similarly, no report is generated if you don't use print options.

PRINT

PRINT:vname:format[:strftime]

Depending on the context, either the value component or the time component of a VDEF is printed using format. It is an error to specify a vname generated by a DEF or CDEF.

Any text in format is printed literally with one exception: The percent character introduces a formatter string. This string can be:

For printing values:

%%

just prints a literal '%' character

%#.#le

prints numbers like 1.2346e+04. The optional integers # denote field width and decimal precision.

%#.#lf

prints numbers like 12345.6789, with optional field width and precision.

%s

place this after %le, %lf or %lg. This will be replaced by the appropriate SI magnitude unit and the value will be scaled accordingly (123456 -> 123.456 k).

%S

is similar to %s. It does, however, use a previously defined magnitude unit. If there is no such unit yet, it tries to define one (just like %s) unless the value is zero, in which case the magnitude unit stays undefined. Thus, formatter strings using %S and no %s will all use the same magnitude unit except for zero values.

If you PRINT a VDEF value, you can also print the time associated with it by appending the string :strftime to the format. Note that RRDtool uses the strftime function of your OSs C library. This means that the conversion specifier may vary. Check the manual page if you are uncertain. The following is a list of conversion specifiers usually supported across the board.

%a

The abbreviated weekday name according to the current locale.

%A

The full weekday name according to the current locale.

%b

The abbreviated month name according to the current locale.

%B

The full month name according to the current locale.

%c

The preferred date and time representation for the current locale.

%d

The day of the month as a decimal number (range 01 to 31).

%H

The hour as a decimal number using a 24-hour clock (range 00 to 23).

%I

The hour as a decimal number using a 12-hour clock (range 01 to 12).

%j

The day of the year as a decimal number (range 001 to 366).

%m

The month as a decimal number (range 01 to 12).

%M

The minute as a decimal number (range 00 to 59).

%p

Either `AM' or `PM' according to the given time value, or the corresponding strings for the current locale. Noon is treated as `pm' and midnight as `am'. Note that in many locales and `pm' notation is unsupported and in such cases %p will return an empty string.

%s

The second as a decimal number (range 00 to 61).

%S

The seconds since the epoch (1.1.1970) (libc dependent non standard!)

%U

The week number of the current year as a decimal number, range 00 to 53, starting with the first Sunday as the first day of week 01. See also %V and %W.

%V

The ISO 8601:1988 week number of the current year as a decimal number, range 01 to 53, where week 1 is the first week that has at least 4 days in the current year, and with Monday as the first day of the week. See also %U and %W.

%w

The day of the week as a decimal, range 0 to 6, Sunday being 0. See also %u.

%W

The week number of the current year as a decimal number, range 00 to 53, starting with the first Monday as the first day of week 01.

%x

The preferred date representation for the current locale without the time.

%X

The preferred time representation for the current locale without the date.

%y

The year as a decimal number without a century (range 00 to 99).

%Y

The year as a decimal number including the century.

%Z

The time zone or name or abbreviation.

%%

A literal `%' character.

PRINT:vname:CF:format

Deprecated. Use the new form of this command in new scripts. The first form of this command is to be used with CDEF vnames.

GRAPH

GPRINT:vname:format

This is the same as PRINT, but printed inside the graph.

GPRINT:vname:CF:format

Deprecated. Use the new form of this command in new scripts. This is the same as PRINT, but printed inside the graph.

COMMENT:text

Text is printed literally in the legend section of the graph. Note that in RRDtool 1.2 you have to escape colons in COMMENT text in the same way you have to escape them in *PRINT commands by writing '\:'.

VRULE:time#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]]

Draw a vertical line at time. Its color is composed from three hexadecimal numbers specifying the rgb color components (00 is off, FF is maximum) red, green and blue followed by an optional alpha. Optionally, a legend box and string is printed in the legend section. time may be a number or a variable from a VDEF. It is an error to use vnames from DEF or CDEF here. Dashed lines can be drawn using the dashes modifier. See LINE for more details.

HRULE:value#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]]

Draw a horizontal line at value. HRULE acts much like LINE except that will have no effect on the scale of the graph. If a HRULE is outside the graphing area it will just not be visible.

LINE[width]:value[#color][:[legend][:STACK]][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]]

Draw a line of the specified width onto the graph. width can be a floating point number. If the color is not specified, the drawing is done 'invisibly'. This is useful when stacking something else on top of this line. Also optional is the legend box and string which will be printed in the legend section if specified. The value can be generated by DEF, VDEF, and CDEF. If the optional STACK modifier is used, this line is stacked on top of the previous element which can be a LINE or an AREA.

The dashes modifier enables dashed line style. Without any further options a symmetric dashed line with a segment length of 5 pixels will be drawn. The dash pattern can be changed if the dashes= parameter is followed by either one value or an even number (1, 2, 4, 6, ...) of positive values. Each value provides the length of alternate on_s and off_s portions of the stroke. The dash-offset parameter specifies an offset into the pattern at which the stroke begins.

When you do not specify a color, you cannot specify a legend. Should you want to use STACK, use the "LINEx:<value>::STACK" form.

AREA:value[#color][:[legend][:STACK]]

See LINE, however the area between the x-axis and the line will be filled.

TICK:vname#rrggbb[aa][:fraction[:legend]]

Plot a tick mark (a vertical line) for each value of vname that is non-zero and not *UNKNOWN*. The fraction argument specifies the length of the tick mark as a fraction of the y-axis; the default value is 0.1 (10% of the axis). Note that the color specification is not optional. The TICK marks normally start at the lower edge of the graphing area. If the fraction is negative they start at the upper border of the graphing area.

SHIFT:vname:offset

Using this command RRDtool will graph the following elements with the specified offset. For instance, you can specify an offset of ( 7*24*60*60 = ) 604'800 seconds to "look back" one week. Make sure to tell the viewer of your graph you did this ... As with the other graphing elements, you can specify a number or a variable here.

TEXTALIGN:{left|right|justified|center}

Labels are placed below the graph. When they overflow to the left, they wrap to the next line. By default, lines are justified left and right. The TEXTALIGN function lets you change this default. This is a command and not an option, so that you can change the default several times in your argument list.

STACK:vname#color[:legend]

Deprecated. Use the STACK modifiers on the other commands instead!

Some notes on stacking

When stacking, an element is not placed above the X-axis but rather on top of the previous element. There must be something to stack upon.

You can use an invisible LINE or AREA to stacked upon.

An unknown value makes the entire stack unknown from that moment on. You don't know where to begin (the unknown value) and therefore do not know where to end.

If you want to make sure you will be displaying a certain variable, make sure never to stack upon the unknown value. Use a CDEF instruction with IF and UN to do so.


NOTES on legend arguments

Escaping the colon

A colon ':' in a legend argument will mark the end of the legend. To enter a ':' as part of a legend, the colon must be escaped with a backslash '\:'. Beware that many environments process backslashes themselves, so it may be necessary to write two backslashes in order to one being passed onto rrd_graph.

String Formatting

The text printed below the actual graph can be formatted by appending special escape characters at the end of a text. When ever such a character occurs, all pending text is pushed onto the graph according to the character specified.

Valid markers are: \j for justified, \l for left aligned, \r for right aligned, and \c for centered. In the next section there is an example showing how to use centered formatting.

\n is a valid alias for \l since incomplete parsing in earlier versions of RRDtool lead to this behavior and a number of people has been using it.

Normally there are two space characters inserted between every two items printed into the graph. The space following a string can be suppressed by putting a \g at the end of the string. The \g also ignores any space inside the string if it is at the very end of the string. This can be used in connection with %s to suppress empty unit strings.

 GPRINT:a:MAX:%lf%s\g

A special case is COMMENT:\s which inserts some additional vertical space before placing the next row of legends.

If you want to have left and right aligned legends on the same line use COMMENT:\u to go one line back like this:

 COMMENT:left\l
 COMMENT:\u
 COMMENT:right\r

There is also a 'nop' control for situations where you want a string to actually end in a backslash character sequence \.

 COMMENT:OS\2\.

When using a proportional font in your graph, the tab characters or the sequence \t will line-up legend elements. Note that the tabs inserted are relative to the start of the current legend element!

Since RRDtool 1.3 is using Pango for rending text, you can use Pango markup. Pango uses the xml span tags for inline formatting instructions.:

A simple example of a marked-up string might be:

 <span foreground="blue" size="x-large">Blue text</span> is <i>cool</i>!

The complete list of attributes for the span tag (taken from the pango documentation):

font_desc

A font description string, such as "Sans Italic 12"; note that any other span attributes will override this description. So if you have "Sans Italic" and also a style="normal" attribute, you will get Sans normal, not italic.

font_family

A font family name

face

Synonym for font_family

size

Font size in 1024ths of a point, or one of the absolute sizes 'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', or one of the relative sizes 'smaller' or 'larger'. If you want to specify a absolute size, it's usually easier to take advantage of the ability to specify a partial font description using 'font_desc'; you can use font_desc='12.5' rather than size='12800'.

style

One of 'normal', 'oblique', 'italic'

weight

One of 'ultralight', 'light', 'normal', 'bold', 'ultrabold', 'heavy', or a numeric weight

variant

'normal' or 'smallcaps'

stretch

One of 'ultracondensed', 'extracondensed', 'condensed', 'semicondensed', 'normal', 'semiexpanded', 'expanded', 'extraexpanded', 'ultraexpanded'

foreground

An RGB color specification such as '#00FF00' or a color name such as 'red'

background

An RGB color specification such as '#00FF00' or a color name such as 'red'

underline

One of 'none', 'single', 'double', 'low', 'error'

underline_color

The color of underlines; an RGB color specification such as '#00FF00' or a color name such as 'red'

rise

Vertical displacement, in 10000ths of an em. Can be negative for subscript, positive for superscript.

strikethrough

'true' or 'false' whether to strike through the text

strikethrough_color

The color of crossed out lines; an RGB color specification such as '#00FF00' or a color name such as 'red'

fallback

'true' or 'false' whether to enable fallback. If disabled, then characters will only be used from the closest matching font on the system. No fallback will be done to other fonts on the system that might contain the characters in the text. Fallback is enabled by default. Most applications should not disable fallback.

lang

A language code, indicating the text language

letter_spacing

Inter-letter spacing in 1024ths of a point.

gravity

One of 'south', 'east', 'north', 'west', 'auto'.

gravity_hint

One of 'natural', 'strong', 'line'.

To save you some typing, there are also some shortcuts:

b

Bold

big

Makes font relatively larger, equivalent to <span size="larger">

i

Italic

s

Strike through

sub

Subscript

sup

Superscript

small

Makes font relatively smaller, equivalent to <span size="smaller">

tt

Monospace font

u

Underline


SEE ALSO

the rrdgraph manpage gives an overview of how rrdtool graph works. the rrdgraph_data manpage describes DEF,CDEF and VDEF in detail. the rrdgraph_rpn manpage describes the RPN language used in the ?DEF statements. the rrdgraph_graph manpage page describes all of the graph and print functions.

Make sure to read the rrdgraph_examples manpage for tips&tricks.


AUTHOR

Program by Tobias Oetiker <tobi@oetiker.ch>

This manual page by Alex van den Bogaerdt <alex@vandenbogaerdt.nl> with corrections and/or additions by several people

rrdtool-1.4.7/doc/rrdfetch.html0000644000175300017510000003546311707501544013410 00000000000000 rrdfetch

NAME

rrdfetch - Fetch data from an RRD.


SYNOPSIS

rrdtool fetch filename CF [--resolution|-r resolution] [--start|-s start] [--end|-e end] [--daemon address]


DESCRIPTION

The fetch function is normally used internally by the graph function to get data from RRDs. fetch will analyze the RRD and try to retrieve the data in the resolution requested. The data fetched is printed to stdout. *UNKNOWN* data is often represented by the string "NaN" depending on your OS's printf function.

filename

the name of the RRD you want to fetch the data from.

CF

the consolidation function that is applied to the data you want to fetch (AVERAGE,MIN,MAX,LAST)

--resolution|-r resolution (default is the highest resolution)

the interval you want the values to have (seconds per value). rrdfetch will try to match your request, but it will return data even if no absolute match is possible. NB. See note below.

--start|-s start (default end-1day)

start of the time series. 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 the start time.

--end|-e end (default now)

the end of the time series in seconds since epoch. See also AT-STYLE TIME SPECIFICATION section for a detailed explanation of how to specify the end time.

--daemon address

Address of the the rrdcached manpage daemon. If specified, a flush command is sent to the server before reading the RRD files. This allows rrdtool to return fresh data even if the daemon is configured to cache values for a long time. For a list of accepted formats, see the -l option in the the rrdcached manpage manual.

 rrdtool fetch --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd AVERAGE

RESOLUTION INTERVAL

In order to get RRDtool to fetch anything other than the finest resolution RRA both the start and end time must be specified on boundaries that are multiples of the desired 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 to fetch the 15 minute average data for the last hour. You might try

 rrdtool fetch subdata.rrd AVERAGE -r 900 -s -1h

However, this will almost always result in a time series that is NOT 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

  1. both start and end time are a multiple of 900

  2. both start and end time are within the desired RRA

So, if time now is called "t", do

 end time == int(t/900)*900,
 start time == end time - 1hour,
 resolution == 900.

Using the bash shell, this could look be:

 TIME=$(date +%s)
 RRDRES=900
 rrdtool fetch subdata.rrd AVERAGE -r $RRDRES \
    -e $(($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"'

AT-STYLE TIME SPECIFICATION

Apart from the traditional Seconds since epoch, RRDtool does also understand at-style time specification. The specification is called "at-style" after the Unix command at(1) that has moderately complex ways to specify time to run your job at a certain date and time. The at-style specification consists of two parts: the TIME REFERENCE specification and the TIME OFFSET specification.

TIME REFERENCE SPECIFICATION

The time reference specification is used, well, to establish a reference moment in time (to which the time offset is then applied to). When present, it should come first, when omitted, it defaults to now. On its own part, time reference consists of a time-of-day reference (which should come first, if present) and a day reference.

The time-of-day can be specified as HH:MM, HH.MM, or just HH. You can suffix it with am or pm or use 24-hours clock. Some special times of day are understood as well, including midnight (00:00), noon (12:00) and British teatime (16:00).

The day can be specified as month-name day-of-the-month and optional a 2- or 4-digit year number (e.g. March 8 1999). Alternatively, you can use day-of-week-name (e.g. Monday), or one of the words: yesterday, today, tomorrow. You can also specify the day as a full date in several numerical formats, including MM/DD/[YY]YY, DD.MM.[YY]YY, or YYYYMMDD.

NOTE1: this is different from the original at(1) behavior, where a single-number date is interpreted as MMDD[YY]YY.

NOTE2: if you specify the day in this way, the time-of-day is REQUIRED as well.

Finally, you can use the words now, start, end or epoch as your time reference. Now refers to the current moment (and is also the default time reference). Start (end) can be used to specify a time relative to the start (end) time for those tools that use these categories (rrdfetch, the rrdgraph manpage) and epoch indicates the *IX epoch (*IX timestamp 0 = 1970-01-01 00:00:00 UTC). epoch is useful to disambiguate between a timestamp value and some forms of abbreviated date/time specifications, because it allows to use time offset specifications using units, eg. epoch+19711205s unambiguously denotes timestamp 19711205 and not 1971-12-05 00:00:00 UTC.

Month and day of the week names can be used in their naturally abbreviated form (e.g., Dec for December, Sun for Sunday, etc.). The words now, start, end can be abbreviated as n, s, e.

TIME OFFSET SPECIFICATION

The time offset specification is used to add/subtract certain time intervals to/from the time reference moment. It consists of a sign (+ or -) and an amount. The following time units can be used to specify the amount: years, months, weeks, days, hours, minutes, or seconds. These units can be used in singular or plural form, and abbreviated naturally or to a single letter (e.g. +3days, -1wk, -3y). Several time units can be combined (e.g., -5mon1w2d) or concatenated (e.g., -5h45min = -5h-45min = -6h+15min = -7h+1h30m-15min, etc.)

NOTE3: If you specify time offset in days, weeks, months, or years, you will end with the time offset that may vary depending on your time reference, because all those time units have no single well defined time interval value (1 year contains either 365 or 366 days, 1 month is 28 to 31 days long, and even 1 day may be not equal to 24 hours twice a year, when DST-related clock adjustments take place). To cope with this, when you use days, weeks, months, or years as your time offset units your time reference date is adjusted accordingly without too much further effort to ensure anything about it (in the hope that mktime(3) will take care of this later). This may lead to some surprising (or even invalid!) results, e.g. 'May 31 -1month' = 'Apr 31' (meaningless) = 'May 1' (after mktime(3) normalization); in the EET timezone '3:30am Mar 29 1999 -1 day' yields '3:30am Mar 28 1999' (Sunday) which is an invalid time/date combination (because of 3am -> 4am DST forward clock adjustment, see the below example).

In contrast, hours, minutes, and seconds are well defined time intervals, and these are guaranteed to always produce time offsets exactly as specified (e.g. for EET timezone, '8:00 Mar 27 1999 +2 days' = '8:00 Mar 29 1999', but since there is 1-hour DST forward clock adjustment that occurs around 3:00 Mar 28 1999, the actual time interval between 8:00 Mar 27 1999 and 8:00 Mar 29 1999 equals 47 hours; on the other hand, '8:00 Mar 27 1999 +48 hours' = '9:00 Mar 29 1999', as expected)

NOTE4: The single-letter abbreviation for both months and minutes is m. To disambiguate them, the parser tries to read your mind :) by applying the following two heuristics:

  1. If m is used in context of (i.e. right after the) years, months, weeks, or days it is assumed to mean months, while in the context of hours, minutes, and seconds it means minutes. (e.g., in -1y6m or +3w1m m is interpreted as months, while in -3h20m or +5s2m m the parser decides for minutes).

  2. Out of context (i.e. right after the + or - sign) the meaning of m is guessed from the number it directly follows. Currently, if the number's absolute value is below 25 it is assumed that m means months, otherwise it is treated as minutes. (e.g., -25m == -25 minutes, while +24m == +24 months)

Final NOTES: Time specification is case-insensitive. Whitespace can be inserted freely or omitted altogether. There are, however, cases when whitespace is required (e.g., 'midnight Thu'). In this case you should either quote the whole phrase to prevent it from being taken apart by your shell or use '_' (underscore) or ',' (comma) which also count as whitespace (e.g., midnight_Thu or midnight,Thu).

TIME SPECIFICATION EXAMPLES

Oct 12 -- October 12 this year

-1month or -1m -- current time of day, only a month before (may yield surprises, see NOTE3 above).

noon yesterday -3hours -- yesterday morning; can also be specified as 9am-1day.

23:59 31.12.1999 -- 1 minute to the year 2000.

12/31/99 11:59pm -- 1 minute to the year 2000 for imperialists.

12am 01/01/01 -- start of the new millennium

end-3weeks or e-3w -- 3 weeks before end time (may be used as start time specification).

start+6hours or s+6h -- 6 hours after start time (may be used as end time specification).

931225537 -- 18:45 July 5th, 1999 (yes, seconds since 1970 are valid as well).

19970703 12:45 -- 12:45 July 3th, 1997 (my favorite, and its even got an ISO number (8601)).


ENVIRONMENT VARIABLES

The following environment variables may be used to change the behavior of rrdtoolfetch:

RRDCACHED_ADDRESS

If this environment variable is set it will have the same effect as specifying the --daemon option on the command line. If both are present, the command line argument takes precedence.


AUTHOR

Tobias Oetiker <tobi@oetiker.ch>

rrdtool-1.4.7/doc/cdeftutorial.html0000644000175300017510000011421511707501544014265 00000000000000 cdeftutorial

NAME

cdeftutorial - Alex van den Bogaerdt's CDEF tutorial


DESCRIPTION

Intention of this document: to provide some examples of the commonly used parts of RRDtool's CDEF language.

If you think some important feature is not explained properly, and if adding it to this document would benefit most users, please do ask me to add it. I will then try to provide an answer in the next release of this tutorial. No feedback equals no changes! Additions to this document are also welcome. -- Alex van den Bogaerdt <alex@vandenbogaerdt.nl>

Why this tutorial?

One of the powerful parts of RRDtool is its ability to do all sorts of calculations on the data retrieved from its 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.

More reading

If you have difficulties with the way I try to explain it please read Steve Rader's the rpntutorial manpage. It may help you understand how this all works.


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.


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,*

This tells RRDtool 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 the variable name used in the CDEF (inbits) must not be the same as the variable named in the DEF (inbytes)!


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:

  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. ||

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.


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 mathematical 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:

   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 evaluated 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,+. This is called the commutative law of addition, but you may forget this right away, as long as you remember what it means.

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 must 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.


Some special numbers

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(s) 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!

Suppose the counter increments with one per second and you retrieve it every minute:

   counter value    resulting rate
   10'000
   10'060            1; (10'060-10'000)/60 == 1
   10'120            1; (10'120-10'060)/60 == 1
   unknown           unknown; you don't know the last value
   10'240            unknown; you don't know the previous value
   10'300            1; (10'300-10'240)/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.

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 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 below that make this change.

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 can think of positive and negative infinity 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.

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.


Some examples

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:

  • 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.

  • Alternatively, 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 manually. Implementing the second method is described next:

What we want is: "if the value is unknown, replace it with zero". This could be written in pseudo-code as: if (value is unknown) then (zero) else (value). When reading the the rrdgraph manpage 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 Rader's RPN guide if you have difficulties with the way I explained this last example.

If you want to check this RPN expression, just mimic RRDtool 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.

Example: better handling of unknown data, by using time

The 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:

  • If there is unknown data, look at the time that this sample was taken.

  • If the unknown value is before time xxx, make it zero.

  • 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, 1999, 00:35:57 MET DST. Translate this time in seconds since 1970-01-01 and it becomes 937'521'357. 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, 1999, 00:35:57 MET DST into 937'521'357 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 937'521'357, 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"

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 thoroughly 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.

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:

  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.

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 1'000 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

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 logical AND and logical 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,GT,TIME,endtime,LE,*,ds0,UNKN,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.

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

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 writing two CDEFS:

  • It improves the readability of the script.

  • 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"


The examples from the RRD graph manual page

Degrees Celsius vs. Degrees Fahrenheit

To convert Celsius into Fahrenheit use the formula F=9/5*C+32

   rrdtool graph demo.png --title="Demo Graph" \
      DEF:cel=demo.rrd:exhaust:AVERAGE \
      CDEF:far=9,5,/,cel,*,32,+ \
      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=9,5,/,cel,*,32,+
   1. push 9, push 5
   2. push function "divide" and process it
      the stack now contains 9/5
   3. push variable "cel"
   4. push function "multiply" and process it
      the stack now contains 9/5*cel
   5. push 32
   6. push function "plus" and process it
      the stack contains now the temperature in Fahrenheit

Changing unknown into zero

   rrdtool graph demo.png --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.

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 effect 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 7'200 (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 earlier 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,*"

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=var,POP,TIME
    CDEF:prevtime=PREV(time)
    CDEF:derivate=var,prev1,-,time,prevtime,-,/
    LINE3:derivate#000077:derivate
    LINE1:var#007700:'raw data'


Out of ideas for now

This document was created from questions asked by either myself or by other people on the RRDtool mailing 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: <alex@vandenbogaerdt.nl>

Remember: No feedback equals no changes!


SEE ALSO

The RRDtool manpages


AUTHOR

Alex van den Bogaerdt <alex@vandenbogaerdt.nl>

rrdtool-1.4.7/doc/rrdupdate.html0000644000175300017510000002166211707501543013574 00000000000000 rrdupdate

NAME

rrdupdate - Store a new set of values into the RRD


SYNOPSIS

rrdtool {update | updatev} filename [--template|-t ds-name[:ds-name]...] [--daemon address] [--] N|timestamp:value[:value...] at-timestamp@value[:value...] [timestamp:value[:value...] ...]


DESCRIPTION

The update function feeds new data values into an RRD. The data is time aligned (interpolated) according to the properties of the RRD to which the data is written.

updatev

This alternate version of update takes the same arguments and performs the same function. The v stands for verbose, which describes the output returned. updatev returns a list of any and all consolidated data points (CDPs) written to disk as a result of the invocation of update. The values are indexed by timestamp (time_t), RRA (consolidation function and PDPs per CDP), and data source (name). Note that depending on the arguments of the current and previous call to update, the list may have no entries or a large number of entries.

Since updatev requires direct disk access, the --daemon option cannot be used with this command.

filename

The name of the RRD you want to update.

--template|-t ds-name[:ds-name]...

By default, the update function expects its data input in the order the data sources are defined in the RRD, excluding any COMPUTE data sources (i.e. if the third data source DST is COMPUTE, the third input value will be mapped to the fourth data source in the RRD and so on). This is not very error resistant, as you might be sending the wrong data into an RRD.

The template switch allows you to specify which data sources you are going to update and in which order. If the data sources specified in the template are not available in the RRD file, the update process will abort with an error message.

While it appears possible with the template switch to update data sources asynchronously, RRDtool implicitly assigns non-COMPUTE data sources missing from the template the *UNKNOWN* value.

Do not specify a value for a COMPUTE DST in the update function. If this is done accidentally (and this can only be done using the template switch), RRDtool will ignore the value specified for the COMPUTE DST.

--daemon address

If given, RRDTool will try to connect to the caching daemon the rrdcached manpage at address and will fail if the connection cannot be established. If the connection is successfully established the values will be sent to the daemon instead of accessing the files directly.

For a list of accepted formats, see the -l option in the the rrdcached manpage manual.

N|timestamp:value[:value...]

The data used for updating the RRD was acquired at a certain time. This time can either be defined in seconds since 1970-01-01 or by using the letter 'N', in which case the update time is set to be the current time. Negative time values are subtracted from the current time. An AT_STYLE TIME SPECIFICATION (see the rrdfetch documentation) may also be used by delimiting the end of the time specification with the '@' character instead of a ':'. Getting the timing right to the second is especially important when you are working with data-sources of type COUNTER, DERIVE or ABSOLUTE.

When using negative time values, options and data have to be separated by two dashes (--), else the time value would be parsed as an option. See below for an example.

When using negative time values, options and data have to be separated by two dashes (--), else the time value would be parsed as an option. See below for an example.

The remaining elements of the argument are DS updates. The order of this list is the same as the order the data sources were defined in the RRA. If there is no data for a certain data-source, the letter U (e.g., N:0.1:U:1) can be specified.

The format of the value acquired from the data source is dependent on the data source type chosen. Normally it will be numeric, but the data acquisition modules may impose their very own parsing of this parameter as long as the colon (:) remains the data source value separator.


ENVIRONMENT VARIABLES

The following environment variables may be used to change the behavior of rrdtoolupdate:

RRDCACHED_ADDRESS

If this environment variable is set it will have the same effect as specifying the --daemon option on the command line. If both are present, the command line argument takes precedence.


EXAMPLES

  • rrdtool update demo1.rrd N:3.44:3.15:U:23

    Update the database file demo1.rrd with 3 known and one *UNKNOWN* value. Use the current time as the update time.

  • rrdtool update demo2.rrd 887457267:U 887457521:22 887457903:2.7

    Update the database file demo2.rrd which expects data from a single data-source, three times. First with an *UNKNOWN* value then with two regular readings. The update interval seems to be around 300 seconds.

  • rrdtool update demo3.rrd -- -5:21 N:42

    Update the database file demo3.rrd two times, using five seconds in the past and the current time as the update times.

  • rrdtool update --cache /var/lib/rrd/demo3.rrd N:42

    Update the file /var/lib/rrd/demo3.rrd with a single data source, using the current time. If the caching daemon cannot be reached, do not fall back to direct file access.

  • rrdtool update --daemon unix:/tmp/rrdd.sock demo4.rrd N:23

    Use the UNIX domain socket /tmp/rrdd.sock to contact the caching daemon. If the caching daemon is not available, update the file demo4.rrd directly. WARNING: Since a relative path is specified, the following disturbing effect may occur: If the daemon is available, the file relative to the working directory of the daemon is used. If the daemon is not available, the file relative to the current working directory of the invoking process is used. This may update two different files depending on whether the daemon could be reached or not. Don't do relative paths, kids!


AUTHORS

Tobias Oetiker <tobi@oetiker.ch>, Florian Forster <octo at verplant.org>

rrdtool-1.4.7/doc/rrdrestore.html0000644000175300017510000000415411707501543013772 00000000000000 rrdrestore

NAME

rrdrestore - Restore the contents of an RRD from its XML dump format


SYNOPSIS

rrdtool restore filename.xml filename.rrd [--range-check|-r]


DESCRIPTION

The restore function reads the XML representation of an RRD and converts it to the native RRD format.

filename.xml

The name of the XML file you want to restore.

filename.rrd

The name of the RRD to restore.

--range-check|-r

Make sure the values in the RRAs do not exceed the limits defined for the various data sources.

--force-overwrite|-f

Allows RRDtool to overwrite the destination RRD.


AUTHOR

Tobias Oetiker <tobi@oetiker.ch>

rrdtool-1.4.7/doc/rrdgraph_examples.html0000644000175300017510000002360411707501543015307 00000000000000 rrdgraph_examples

NAME

rrdgraph_examples - Examples for rrdtool graph


SYNOPSIS

rrdtool graph /home/httpd/html/test.png --img-format PNG

followed by any of the examples below


DESCRIPTION

For your convenience some of the commands are explained here by using detailed examples. They are not always cut-and-paste ready because comments are intermixed with the examples.


EXAMPLES

Data with multiple resolutions

    --end now --start end-120000s --width 400
    DEF:ds0a=/home/rrdtool/data/router1.rrd:ds0:AVERAGE
    DEF:ds0b=/home/rrdtool/data/router1.rrd:ds0:AVERAGE:step=1800
    DEF:ds0c=/home/rrdtool/data/router1.rrd:ds0:AVERAGE:step=7200
    LINE1:ds0a#0000FF:"default resolution\l"
    LINE1:ds0b#00CCFF:"resolution 1800 seconds per interval\l"
    LINE1:ds0c#FF00FF:"resolution 7200 seconds per interval\l"

Nicely formatted legend section

    DEF:ds0=/home/rrdtool/data/router1.rrd:ds0:AVERAGE
    DEF:ds1=/home/rrdtool/data/router1.rrd:ds1:AVERAGE
    VDEF:ds0max=ds0,MAXIMUM
    VDEF:ds0avg=ds0,AVERAGE
    VDEF:ds0min=ds0,MINIMUM
    VDEF:ds0pct=ds0,95,PERCENT
    VDEF:ds1max=ds1,MAXIMUM
    VDEF:ds1avg=ds1,AVERAGE
    VDEF:ds1min=ds1,MINIMUM
    VDEF:ds1pct=ds1,95,PERCENT

Note: consolidation occurs here.

    CDEF:ds0bits=ds0,8,*
    CDEF:ds1bits=ds1,8,*

Note: 10 spaces to move text to the right

    COMMENT:"          "

Note: the column titles have to be as wide as the columns

    COMMENT:"Maximum    "
    COMMENT:"Average    "
    COMMENT:"Minimum    "
    COMMENT:"95th percentile\l"
    AREA:ds0bits#00C000:"Inbound "
    GPRINT:ds0max:"%6.2lf %Sbps"
    GPRINT:ds0avg:"%6.2lf %Sbps"
    GPRINT:ds0min:"%6.2lf %Sbps"
    GPRINT:ds0pct:"%6.2lf %Sbps\l"
    LINE1:ds1bits#0000FF:"Outbound"
    GPRINT:ds1max:"%6.2lf %Sbps"
    GPRINT:ds1avg:"%6.2lf %Sbps"
    GPRINT:ds1min:"%6.2lf %Sbps"
    GPRINT:ds1pct:"%6.2lf %Sbps\l"

Offsetting a line on the y-axis

Depending on your needs you can do this in two ways:

  • Offset the data, then graph this

        DEF:mydata=my.rrd:ds:AVERAGE

    Note: this will also influence any other command that uses "data"

        CDEF:data=mydata,100,+
        LINE1:data#FF0000:"Data with offset"
  • Graph the original data, with an offset

        DEF:mydata=my.rrd:ds:AVERAGE

    Note: no color in the first line so it is not visible

        LINE1:100

    Note: the second line gets stacked on top of the first one

        LINE1:data#FF0000:"Data with offset":STACK

Drawing dashed lines

Also works for HRULE and VRULE

  • default style: - - - - - LINE1:data#FF0000:"dashed line":dashes

  • more fancy style with offset: - - --- - --- - LINE1:data#FF0000:"another dashed line":dashes=15,5,5,10:dash-offset=10

Time ranges

    Last four weeks: --start end-4w --end 00:00
    January 2001:    --start 20010101 --end start+31d
    January 2001:    --start 20010101 --end 20010201
    Last hour:       --start end-1h
    Last 24 hours:   <nothing at all>
    Yesterday:       --end 00:00

Viewing the current and previous week together

    --end now --start end-1w
    DEF:thisweek=router.rrd:ds0:AVERAGE
    DEF:lastweek=router.rrd:ds0:AVERAGE:end=now-1w:start=end-1w

Shift the data forward by one week (604800 seconds)

    SHIFT:lastweek:604800
    [ more of the usual VDEF and CDEF stuff if you like ]
    AREA:lastweek#0000FF:Last\ week
    LINE1:thisweek#FF0000:This\ week

Aberrant Behaviour Detection

If the specialized function RRAs exist for aberrant behavior detection, they can be used to generate the graph of a time series with confidence bands and failures.

   rrdtool graph example.png \
          DEF:obs=monitor.rrd:ifOutOctets:AVERAGE \
          DEF:pred=monitor.rrd:ifOutOctets:HWPREDICT \
          DEF:dev=monitor.rrd:ifOutOctets:DEVPREDICT \
          DEF:fail=monitor.rrd:ifOutOctets:FAILURES \
          TICK:fail#ffffa0:1.0:"Failures\: Average bits out" \
          CDEF:scaledobs=obs,8,* \
          CDEF:upper=pred,dev,2,*,+ \
          CDEF:lower=pred,dev,2,*,- \
          CDEF:scaledupper=upper,8,* \
          CDEF:scaledlower=lower,8,* \
          LINE2:scaledobs#0000ff:"Average bits out" \
          LINE1:scaledupper#ff0000:"Upper Confidence Bound: Average bits out" \
          LINE1:scaledlower#ff0000:"Lower Confidence Bound: Average bits out"

This example generates a graph of the data series in blue (LINE2 with the scaledobs virtual data source), confidence bounds in red (scaledupper and scaledlower virtual data sources), and potential failures (i.e. potential aberrant aberrant behavior) marked by vertical yellow lines (the fail data source).

The raw data comes from an AVERAGE RRA, the finest resolution of the observed time series (one consolidated data point per primary data point). The predicted (or smoothed) values are stored in the HWPREDICT RRA. The predicted deviations (think standard deviation) values are stored in the DEVPREDICT RRA. Finally, the FAILURES RRA contains indicators, with 1 denoting a potential failure.

All of the data is rescaled to bits (instead of Octets) by multiplying by 8. The confidence bounds are computed by an offset of 2 deviations both above and below the predicted values (the CDEFs upper and lower). Vertical lines indicated potential failures are graphed via the TICK graph element, which converts non-zero values in an RRA into tick marks. Here an axis-fraction argument of 1.0 means the tick marks span the entire y-axis, and hence become vertical lines on the graph.

The choice of 2 deviations (a scaling factor) matches the default used internally by the FAILURES RRA. If the internal value is changed (see the rrdtune manpage), this graphing command should be changed to be consistent.

A note on data reduction:

The rrdtool graph command is designed to plot data at a specified temporal resolution, regardless of the actually resolution of the data in the RRD file. This can present a problem for the specialized consolidation functions which maintain a one-to-one mapping between primary data points and consolidated data points. If a graph insists on viewing the contents of these RRAs on a coarser temporal scale, the graph command tries to do something intelligent, but the confidence bands and failures no longer have the same meaning and may be misleading.


SEE ALSO

the rrdgraph manpage gives an overview of how rrdtool graph works. the rrdgraph_data manpage describes DEF,CDEF and VDEF in detail. the rrdgraph_rpn manpage describes the RPN language used in the xDEF statements. the rrdgraph_graph manpage page describes all the graph and print functions.


AUTHOR

Program by Tobias Oetiker <tobi@oetiker.ch>

This manual page by Alex van den Bogaerdt <alex@vandenbogaerdt.nl> with corrections and/or additions by several people

rrdtool-1.4.7/doc/rrddump.html0000644000175300017510000001053511707501543013254 00000000000000 rrddump

NAME

rrddump - dump the contents of an RRD to XML format


SYNOPSIS

rrdtool dump filename.rrd [filename.xml] [--header|-h {none,xsd,dtd}] [--no-header] [--daemon address] > filename.xml


DESCRIPTION

The dump function writes the contents of an RRD in human readable (?) XML format to a file or to stdout. This format can be read by rrdrestore. Together they allow you to transfer your files from one computer architecture to another as well to manipulate the contents of an RRD file in a somewhat more convenient manner.

filename.rrd

The name of the RRD you want to dump.

filename.xml

The (optional) filename that you want to write the XML output to. If not specified, the XML will be printed to stdout.

--header|-h {none,xsd,dtd}

By default RRDtool will add a dtd header to the xml file. Here you can customize this to and xsd header or no header at all.

--no-header

A shortcut for --header=none.

If you want to restore the dump with RRDtool 1.2 you should use the --no-header option since 1.2 can not deal with xml headers.

--daemon address

Address of the the rrdcached manpage daemon. If specified, a flush command is sent to the server before reading the RRD files. This allows rrdtool to return fresh data even if the daemon is configured to cache values for a long time. For a list of accepted formats, see the -l option in the the rrdcached manpage manual.

 rrdtool dump --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd


EXAMPLES

To transfer an RRD between architectures, follow these steps:

  1. On the same system where the RRD was created, use rrdtool dump to export the data to XML format.

  2. Transfer the XML dump to the target system.

  3. Run rrdtool restore to create a new RRD from the XML dump. See rrdrestore for details.


ENVIRONMENT VARIABLES

The following environment variables may be used to change the behavior of rrdtooldump:

RRDCACHED_ADDRESS

If this environment variable is set it will have the same effect as specifying the --daemon option on the command line. If both are present, the command line argument takes precedence.


AUTHOR

Tobias Oetiker <tobi@oetiker.ch>

rrdtool-1.4.7/doc/bin_dec_hex.html0000644000175300017510000003472711707501543014037 00000000000000 bin_dec_hex

NAME

bin_dec_hex - How to use binary, decimal, and hexadecimal notation.


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 to the left. 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 cryptic to you, this is what I've just 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 is incremented, it is reset to 0 and the position before (to the left) is incremented (from 0 to 1). 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". Leading zeros we don't write except if it is the only digit (number 0). And of course, we write zeros if they occur anywhere inside or at the end of a number:

 "00010" -> " 0010" -> " 010" -> "  10", but not "  1 ".

This was pretty basic, you already knew this. Why did I tell it? Well, computers usually do not represent numbers with 10 different digits. They only use two different symbols, namely "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 as in the first list, we just used 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 explicitly specify the numbering system used, we implicitly use the decimal system. If we want to use any other numbering system, we'll have to make that clear. There are a few widely adopted methods to do so. One common form is to write 1010(2) which means that you wrote down a number in its binary representation. It is the number ten. If you would write 1010 without specifying the base, the number is interpreted as one thousand and ten using base 10.

In books, another form is common. It uses subscripts (little characters, 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 by a little two just behind it.

As the numbering system used is also called the base, we talk of the number 1100 base 2, the number 12 base 10.

Within the binary system, it is common to write leading zeros. 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 65'535 (often in the decimal system a ' is used to separate blocks of three digits for readability) would be written down as 1111111111111111(2) which is 16 times the digit 1. This is difficult and prone to errors. Therefore, we usually would use another base, called hexadecimal. It uses 16 different symbols. First the symbols from the decimal system are used, thereafter we continue with 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 easily (and back).

There is yet another system in use, called the octal system. This was more common in the old days, but is not used very often anymore. As you might find it in use sometimes, you should get used to it and we'll show it below. It's the same story as with the other representations, but with 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 practical 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: take four digits from the right and make a hexadecimal digit from it (see the table above). Repeat this until there are no more digits. And the 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 are specified with 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 their representation (base) 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, (no prefix, 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 only uses two symbols. Another example:

%1010 would be

 1 * 2^3
 0 * 2^2
 1 * 2^1
 0 * 2^0

It would have been easier 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 you could use the next method. It will take some time to be able to do the estimates, but it will be easier when you use the system more frequently. We'll look at yet another way afterwards.

First you need to know how many positions will be used in the other system. To do so, you need to know the maximum numbers you'll be using. Well, that's not as 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 as well:

 16^4 = 0x10000 = 65536
 16^3 =  0x1000 =  4096
 16^2 =   0x100 =   256
 16^1 =    0x10 =    16

If a number is smaller than 65'536 it will fit in four positions. If the number is bigger than 4'095, you must use position 4. How many times you can subtract 4'096 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.

Let's try with 41'029. 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 41'029 ten times without going below zero. The left-most digit will therefore be "A", so we have 0xA????. The number is reduced to 41'029 - 10*4'096 = 41'029-40'960 = 69. 69 is smaller than 16^3 but not bigger than 16^2-1. The second digit is therefore "0" and we now have 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?. Subtract 64 from 69 (69 - 4*16) and the last digit is 5 --> 0xA045.

The other method builds up the number from the right. Let's try 41'029 again. Divide by 16 and do not use fractions (only whole numbers).

 41'029 / 16 is 2'564 with a remainder of 5. Write down 5.
 2'564 / 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. 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 to one I am familiar with, then I will use the first method (like 32'770 which is into 32'768 + 2 and I 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 easily if you should write down a zero or a one: if you divide by two the remainder will be zero if it is an even number and one if it is 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 directly in the base that is used. In any representation it is the same, add the numbers on the right, write down the right-most digit from the result, remember the other digits and use them in the next round. Continue with the second digit 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, then check them with a calculator. The more you do the calculations yourself, the more you'll find that you didn't make mistakes. In the end, you'll do calculi in other bases as easily as you do them 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 short for Binary Calculator. It calculates not only in decimal, but in all bases you'll ever want to 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.


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 <alex@vandenbogaerdt.nl>

rrdtool-1.4.7/doc/librrd.pod0000644000175300017510000001046711277765724012716 00000000000000=pod =head1 NAME librrd - RRD library functions =head1 DESCRIPTION B contains most of the functionality in B. The command line utilities and language bindings are often just wrappers around the code contained in B. This manual page documents the B API. B This document is a work in progress, and should be considered incomplete as long as this warning persists. For more information about the B functions, always consult the source code. =head1 CORE FUNCTIONS =over 4 =item B In some situations it is necessary to get the output of C without writing it to a file or the standard output. In such cases an application can ask B to call an user-defined function each time there is output to be stored somewhere. This can be used, to e.g. directly feed an XML parser with the dumped output or transfer the resulting string in memory. The arguments for B are the same as for B except that the output filename parameter is replaced by the user-defined callback function and an additional parameter for the callback function that is passed untouched, i.e. to store information about the callback state needed for the user-defined callback to function properly. Recent versions of B internally use this callback mechanism to write their output to the file provided by the user. size_t rrd_dump_opt_cb_fileout( const void *data, size_t len, void *user) { return fwrite(data, 1, len, (FILE *)user); } The associated call for B looks like res = rrd_dump_cb_r(filename, opt_header, rrd_dump_opt_cb_fileout, (void *)out_file); where the last parameter specifies the file handle B should write to. There's no specific condition for the callback to detect when it is called for the first time, nor for the last time. If you require this for initialization and cleanup you should do those tasks before and after calling B respectively. =back =head1 UTILITY FUNCTIONS =over 4 =item B Generates random numbers just like random(). This further ensures that the random number generator is seeded exactly once per process. =item B Dynamically resize the array pointed to by C. C is a pointer to the current size of C. Upon successful realloc(), the C is incremented by 1 and the C pointer is stored at the end of the new C. Returns 1 on success, 0 on failure. type **arr = NULL; type *elem = "whatever"; size_t arr_size = 0; if (!rrd_add_ptr(&arr, &arr_size, elem)) handle_failure(); =item B Like C, except adds a C of the source string. char **arr = NULL; size_t arr_size = NULL; char *str = "example text"; if (!rrd_add_strdup(&arr, &arr_size, str)) handle_failure(); =item B Free an array of pointers allocated by C or C. Also frees the array pointer itself. On return, the source pointer will be NULL and the count will be zero. /* created as above */ rrd_free_ptrs(&arr, &arr_size); /* here, arr == NULL && arr_size == 0 */ =item B Create the directory named C including all of its parent directories (similar to C on the command line - see L for more information). The argument C specifies the permissions to use. It is modified by the process's C. See L for more details. The function returns 0 on success, a negative value else. In case of an error, C is set accordingly. Aside from the errors documented in L, the function may fail with the following errors: =over 4 =item B C is C or the empty string. =item B Insufficient memory was available. =item B> =back In contrast to L, the function does B fail if C already exists and is a directory. =back =head1 AUTHOR RRD Contributors rrdtool-1.4.7/doc/rrdtutorial.pod0000644000175300017510000014540111514315231013762 00000000000000=for changes please consult me first. Thanks, Alex =head1 NAME rrdtutorial - Alex van den Bogaerdt's RRDtool tutorial =head1 DESCRIPTION RRDtool is written by Tobias Oetiker Etobi@oetiker.chE with contributions from many people all around the world. This document is written by Alex van den Bogaerdt Ealex@vandenbogaerdt.nlE to help you understand what RRDtool is and what it can do for you. The documentation provided with RRDtool can be too technical for some people. This tutorial is here to help you understand the basics of RRDtool. It should prepare you to read the documentation yourself. It also explains the general things about statistics with a focus on networking. =head1 TUTORIAL =head2 Important Please don't skip ahead in this document! The first part of this document explains the basics and may be boring. But if you don't understand the basics, the examples will not be as meaningful to you. Sometimes things change. This example used to provide numbers like "0.04" in stead of "4.00000e-02". Those are really the same numbers, just written down differently. Don't be alarmed if a future version of rrdtool displays a slightly different form of output. The examples in this document are correct for version 1.2.0 of RRDtool. Also, sometimes bugs do occur. They may also influence the outcome of the examples. Example speed4.png was suffering from this (the handling of unknown data in an if-statement was wrong). Normal data will be just fine (a bug in rrdtool wouldn't last long) but special cases like NaN, INF and so on may last a bit longer. Try another version if you can, or just live with it. I fixed the speed4.png example (and added a note). There may be other examples which suffer from the same or a similar bug. Try to fix it yourself, which is a great excercise. But please do not submit your result as a fix to the source of this document. Discuss it on the user's list, or write to me. =head2 What is RRDtool? RRDtool refers to Round Robin Database tool. Round robin is a technique that works with a fixed amount of data, and a pointer to the current element. Think of a circle with some dots plotted on the edge. These dots are the places where data can be stored. Draw an arrow from the center of the circle to one of the dots; this is the pointer. When the current data is read or written, the pointer moves to the next element. As we are on a circle there is neither a beginning nor an end, you can go on and on and on. After a while, all the available places will be used and the process automatically reuses old locations. This way, the dataset will not grow in size and therefore requires no maintenance. RRDtool works with Round Robin Databases (RRDs). It stores and retrieves data from them. =head2 What data can be put into an RRD? You name it, it will probably fit as long as it is some sort of time-series data. This means you have to be able to measure some value at several points in time and provide this information to RRDtool. If you can do this, RRDtool will be able to store it. The values must be numerical but don't have to be integers, as is the case with MRTG (the next section will give more details on this more specialized application). Many examples below talk about SNMP which is an acronym for Simple Network Management Protocol. "Simple" refers to the protocol. It does not mean it is simple to manage or monitor a network. After working your way through this document, you should know enough to be able to understand what people are talking about. For now, just realize that SNMP can be used to query devices for the values of counters they keep. It is the value from those counters that we want to store in the RRD. =head2 What can I do with this tool? RRDtool originated from MRTG (Multi Router Traffic Grapher). MRTG started as a tiny little script for graphing the use of a university's connection to the Internet. MRTG was later (ab-)used as a tool for graphing other data sources including temperature, speed, voltage, number of printouts and the like. Most likely you will start to use RRDtool to store and process data collected via SNMP. The data will most likely be bytes (or bits) transferred from and to a network or a computer. But it can also be used to display tidal waves, solar radiation, power consumption, number of visitors at an exhibition, noise levels near an airport, temperature on your favorite holiday location, temperature in the fridge and whatever your imagination can come up with. You only need a sensor to measure the data and be able to feed the numbers into RRDtool. RRDtool then lets you create a database, store data in it, retrieve that data and create graphs in PNG format for display on a web browser. Those PNG images are dependent on the data you collected and could be, for instance, an overview of the average network usage, or the peaks that occurred. =head2 What if I still have problems after reading this document? First of all: read it again! You may have missed something. If you are unable to compile the sources and you have a fairly common OS, it will probably not be the fault of RRDtool. There may be pre-compiled versions around on the Internet. If they come from trusted sources, get one of those. If on the other hand the program works but does not give you the expected results, it will be a problem with configuring it. Review your configuration and compare it with the examples that follow. There is a mailing list and an archive of it. Read the list for a few weeks and search the archive. It is considered rude to just ask a question without searching the archives: your problem may already have been solved for somebody else! This is true for most, if not all, mailing lists and not only for this particular one. Look in the documentation that came with RRDtool for the location and usage of the list. I suggest you take a moment to subscribe to the mailing list right now by sending an email to Errd-users-request@lists.oetiker.chE with a subject of "subscribe". If you ever want to leave this list, just write an email to the same address but now with a subject of "unsubscribe". =head2 How will you help me? By giving you some detailed descriptions with detailed examples. I assume that following the instructions in the order presented will give you enough knowledge of RRDtool to experiment for yourself. If it doesn't work the first time, don't give up. Reread the stuff that you did understand, you may have missed something. By following the examples you get some hands-on experience and, even more important, some background information of how it works. You will need to know something about hexadecimal numbers. If you don't then start with reading L before you continue here. =head2 Your first Round Robin Database In my opinion the best way to learn something is to actually do it. Why not start right now? We will create a database, put some values in it and extract this data again. Your output should be the same as the output that is included in this document. We will start with some easy stuff and compare a car with a router, or compare kilometers (miles if you wish) with bits and bytes. It's all the same: some number over some time. Assume we have a device that transfers bytes to and from the Internet. This device keeps a counter that starts at zero when it is turned on, increasing with every byte that is transferred. This counter will probably have a maximum value. If this value is reached and an extra byte is counted, the counter starts over at zero. This is the same as many counters in the world such as the mileage counter in a car. Most discussions about networking talk about bits per second so lets get used to that right away. Assume a byte is eight bits and start to think in bits not bytes. The counter, however, still counts bytes! In the SNMP world most of the counters are 32 bits. That means they are counting from 0 to 4294967295. We will use these values in the examples. The device, when asked, returns the current value of the counter. We know the time that has passes since we last asked so we now know how many bytes have been transferred ***on average*** per second. This is not very hard to calculate. First in words, then in calculations: =over 3 =item 1. Take the current counter, subtract the previous value from it. =item 2. Do the same with the current time and the previous time (in seconds). =item 3. Divide the outcome of (1) by the outcome of (2), the result is the amount of bytes per second. Multiply by eight to get the number of bits per second (bps). =back bps = (counter_now - counter_before) / (time_now - time_before) * 8 For some people it may help to translate this to an automobile example. Do not try this example, and if you do, don't blame me for the results! People who are not used to think in kilometers per hour can translate most into miles per hour by dividing km by 1.6 (close enough). I will use the following abbreviations: m: meter km: kilometer (= 1000 meters). h: hour s: second km/h: kilometers per hour m/s: meters per second You are driving a car. At 12:05 you read the counter in the dashboard and it tells you that the car has moved 12345 km until that moment. At 12:10 you look again, it reads 12357 km. This means you have traveled 12 km in five minutes. A scientist would translate that into meters per second and this makes a nice comparison toward the problem of (bytes per five minutes) versus (bits per second). We traveled 12 kilometers which is 12000 meters. We did that in five minutes or 300 seconds. Our speed is 12000m / 300s or 40 m/s. We could also calculate the speed in km/h: 12 times 5 minutes is an hour, so we have to multiply 12 km by 12 to get 144 km/h. For our native English speaking friends: that's 90 mph so don't try this example at home or where I live :) Remember: these numbers are averages only. There is no way to figure out from the numbers, if you drove at a constant speed. There is an example later on in this tutorial that explains this. I hope you understand that there is no difference in calculating m/s or bps; only the way we collect the data is different. Even the k from kilo is the same as in networking terms k also means 1000. We will now create a database where we can keep all these interesting numbers. The method used to start the program may differ slightly from OS to OS, but I assume you can figure it out if it works different on your's. Make sure you do not overwrite any file on your system when executing the following command and type the whole line as one long line (I had to split it for readability) and skip all of the '\' characters. rrdtool create test.rrd \ --start 920804400 \ DS:speed:COUNTER:600:U:U \ RRA:AVERAGE:0.5:1:24 \ RRA:AVERAGE:0.5:6:10 (So enter: C) =head2 What has been created? We created the round robin database called test (test.rrd) which starts at noon the day I started writing this document, 7th of March, 1999 (this date translates to 920804400 seconds as explained below). Our database holds one data source (DS) named "speed" that represents a counter. This counter is read every five minutes (this is the default therefore you don't have to put C<--step=300>). In the same database two round robin archives (RRAs) are kept, one averages the data every time it is read (e.g., there's nothing to average) and keeps 24 samples (24 times 5 minutes is 2 hours). The other averages 6 values (half hour) and contains 10 such averages (e.g. 5 hours). RRDtool works with special time stamps coming from the UNIX world. This time stamp is the number of seconds that passed since January 1st 1970 UTC. The time stamp value is translated into local time and it will therefore look different for different time zones. Chances are that you are not in the same part of the world as I am. This means your time zone is different. In all examples where I talk about time, the hours may be wrong for you. This has little effect on the results of the examples, just correct the hours while reading. As an example: where I will see "12:05" the UK folks will see "11:05". We now have to fill our database with some numbers. We'll pretend to have read the following numbers: 12:05 12345 km 12:10 12357 km 12:15 12363 km 12:20 12363 km 12:25 12363 km 12:30 12373 km 12:35 12383 km 12:40 12393 km 12:45 12399 km 12:50 12405 km 12:55 12411 km 13:00 12415 km 13:05 12420 km 13:10 12422 km 13:15 12423 km We fill the database as follows: rrdtool update test.rrd 920804700:12345 920805000:12357 920805300:12363 rrdtool update test.rrd 920805600:12363 920805900:12363 920806200:12373 rrdtool update test.rrd 920806500:12383 920806800:12393 920807100:12399 rrdtool update test.rrd 920807400:12405 920807700:12411 920808000:12415 rrdtool update test.rrd 920808300:12420 920808600:12422 920808900:12423 This reads: update our test database with the following numbers time 920804700, value 12345 time 920805000, value 12357 etcetera. As you can see, it is possible to feed more than one value into the database in one command. I had to stop at three for readability but the real maximum per line is OS dependent. We can now retrieve the data from our database using "rrdtool fetch": rrdtool fetch test.rrd AVERAGE --start 920804400 --end 920809200 It should return the following output: speed 920804700: nan 920805000: 4.0000000000e-02 920805300: 2.0000000000e-02 920805600: 0.0000000000e+00 920805900: 0.0000000000e+00 920806200: 3.3333333333e-02 920806500: 3.3333333333e-02 920806800: 3.3333333333e-02 920807100: 2.0000000000e-02 920807400: 2.0000000000e-02 920807700: 2.0000000000e-02 920808000: 1.3333333333e-02 920808300: 1.6666666667e-02 920808600: 6.6666666667e-03 920808900: 3.3333333333e-03 920809200: nan 920809500: nan Note that you might get more rows than you expect. The reason for this is that you ask for a time range that ends on 920809200. The number that is written behind 920809200: in the list above covers the time range from 920808900 to 920809200, EXCLUDING 920809200. Hence to be on the sure side, you receive the entry from 920809200 to 920809500 as well since it INCLUDES 920809200. You may also see "NaN" instead of "nan" this is OS dependent. "NaN" stands for "Not A Number". If your OS writes "U" or "UNKN" or something similar that's okay. If something else is wrong, it will probably be due to an error you made (assuming that my tutorial is correct of course :-). In that case: delete the database and try again. The meaning of the above output will become clear below. =head2 Time to create some graphics Try the following command: rrdtool graph speed.png \ --start 920804400 --end 920808000 \ DEF:myspeed=test.rrd:speed:AVERAGE \ LINE2:myspeed#FF0000 This will create speed.png which starts at 12:00 and ends at 13:00. There is a definition of a variable called myspeed, using the data from RRA "speed" out of database "test.rrd". The line drawn is 2 pixels high and represents the variable myspeed. The color is red (specified by its rgb-representation, see below). You'll notice that the start of the graph is not at 12:00 but at 12:05. This is because we have insufficient data to tell the average before that time. This will only happen when you miss some samples, this will not happen a lot, hopefully. If this has worked: congratulations! If not, check what went wrong. The colors are built up from red, green and blue. For each of the components, you specify how much to use in hexadecimal where 00 means not included and FF means fully included. The "color" white is a mixture of red, green and blue: FFFFFF The "color" black is all colors off: 000000 red #FF0000 green #00FF00 blue #0000FF magenta #FF00FF (mixed red with blue) gray #555555 (one third of all components) Additionally you can (with a recent RRDtool) add an alpha channel (transparency). The default will be "FF" which means non-transparent. The PNG you just created can be displayed using your favorite image viewer. Web browsers will display the PNG via the URL "file:///the/path/to/speed.png" =head2 Graphics with some math When looking at the image, you notice that the horizontal axis is labeled 12:10, 12:20, 12:30, 12:40 and 12:50. Sometimes a label doesn't fit (12:00 and 13:00 would be likely candidates) so they are skipped. The vertical axis displays the range we entered. We provided kilometers and when divided by 300 seconds, we get very small numbers. To be exact, the first value was 12 (12357-12345) and divided by 300 this makes 0.04, which is displayed by RRDtool as "40 m" meaning "40/1000". The "m" (milli) has nothing to do with meters (also m), kilometers or millimeters! RRDtool doesn't know about the physical units of our data, it just works with dimensionless numbers. If we had measured our distances in meters, this would have been (12357000-12345000)/300 = 12000/300 = 40. As most people have a better feel for numbers in this range, we'll correct that. We could recreate our database and store the correct data, but there is a better way: we do some calculations while creating the png file! rrdtool graph speed2.png \ --start 920804400 --end 920808000 \ --vertical-label m/s \ DEF:myspeed=test.rrd:speed:AVERAGE \ CDEF:realspeed=myspeed,1000,\* \ LINE2:realspeed#FF0000 Note: I need to escape the multiplication operator * with a backslash. If I don't, the operating system may interpret it and use it for file name expansion. You could also place the line within quotation marks like so: "CDEF:realspeed=myspeed,1000,*" \ It boils down to: it is RRDtool which should see *, not your shell. And it is your shell interpreting \, not RRDtool. You may need to adjust examples accordingly if you happen to use an operating system or shell which behaves differently. After viewing this PNG, you notice the "m" (milli) has disappeared. This it what the correct result would be. Also, a label has been added to the image. Apart from the things mentioned above, the PNG should look the same. The calculations are specified in the CDEF part above and are in Reverse Polish Notation ("RPN"). What we requested RRDtool to do is: "take the data source myspeed and the number 1000; multiply those". Don't bother with RPN yet, it will be explained later on in more detail. Also, you may want to read my tutorial on CDEFs and Steve Rader's tutorial on RPN. But first finish this tutorial. Hang on! If we can multiply values with 1000, it should also be possible to display kilometers per hour from the same data! To change a value that is measured in meters per second: Calculate meters per hour: value * 3600 Calculate kilometers per hour: value / 1000 Together this makes: value * (3600/1000) or value * 3.6 In our example database we made a mistake and we need to compensate for this by multiplying with 1000. Applying that correction: value * 3.6 * 1000 == value * 3600 Now let's create this PNG, and add some more magic ... rrdtool graph speed3.png \ --start 920804400 --end 920808000 \ --vertical-label km/h \ DEF:myspeed=test.rrd:speed:AVERAGE \ "CDEF:kmh=myspeed,3600,*" \ CDEF:fast=kmh,100,GT,kmh,0,IF \ CDEF:good=kmh,100,GT,0,kmh,IF \ HRULE:100#0000FF:"Maximum allowed" \ AREA:good#00FF00:"Good speed" \ AREA:fast#FF0000:"Too fast" Note: here we use another means to escape the * operator by enclosing the whole string in double quotes. This graph looks much better. Speed is shown in km/h and there is even an extra line with the maximum allowed speed (on the road I travel on). I also changed the colors used to display speed and changed it from a line into an area. The calculations are more complex now. For speed measurements within the speed limit they are: Check if kmh is greater than 100 ( kmh,100 ) GT If so, return 0, else kmh ((( kmh,100 ) GT ), 0, kmh) IF For values above the speed limit: Check if kmh is greater than 100 ( kmh,100 ) GT If so, return kmh, else return 0 ((( kmh,100) GT ), kmh, 0) IF =head2 Graphics Magic I like to believe there are virtually no limits to how RRDtool graph can manipulate data. I will not explain how it works, but look at the following PNG: rrdtool graph speed4.png \ --start 920804400 --end 920808000 \ --vertical-label km/h \ DEF:myspeed=test.rrd:speed:AVERAGE \ CDEF:nonans=myspeed,UN,0,myspeed,IF \ CDEF:kmh=nonans,3600,* \ CDEF:fast=kmh,100,GT,100,0,IF \ CDEF:over=kmh,100,GT,kmh,100,-,0,IF \ CDEF:good=kmh,100,GT,0,kmh,IF \ HRULE:100#0000FF:"Maximum allowed" \ AREA:good#00FF00:"Good speed" \ AREA:fast#550000:"Too fast" \ STACK:over#FF0000:"Over speed" Remember the note in the beginning? I had to remove unknown data from this example. The 'nonans' CDEF is new, and the 6th line (which used to be the 5th line) used to read 'CDEF:kmh=myspeed,3600,*' Let's create a quick and dirty HTML page to view the three PNGs: Speed Speed in meters per second
Speed in kilometers per hour
Traveled too fast? Name the file "speed.html" or similar, and look at it in your web browser. Now, all you have to do is measure the values regularly and update the database. When you want to view the data, recreate the PNGs and make sure to refresh them in your browser. (Note: just clicking reload may not be enough, especially when proxies are involved. Try shift-reload or ctrl-F5). =head2 Updates in Reality We've already used the C command: it took one or more parameters in the form of "