RubyRRDtool 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/Manifest.txt ADDED
@@ -0,0 +1,9 @@
1
+ README
2
+ Manifest.txt
3
+ Rakefile
4
+ extconf.rb
5
+ rrd_addition.h
6
+ rubyrrdtool.c
7
+ test/test_rrdtool.rb
8
+ examples/function_test.rb
9
+ examples/minmax.rb
data/README ADDED
@@ -0,0 +1,64 @@
1
+ # README -- ruby librrd bindings
2
+
3
+ Authors: David Bacher <drbacher at alum.mit.edu>
4
+ Mark Probert <probertm at acm.org>
5
+ based on work by Miles Egan <miles at caddr.com>
6
+
7
+
8
+ == Introduction
9
+
10
+ rubyrrdtool provides ruby bindings for RRD functions (via librrd), with
11
+ functionality comparable to the native perl bindings. See examples/minmax.rb
12
+ or the unit tests in the test directory for scripts that exercise the
13
+ rrdtool functions.
14
+
15
+
16
+ == Installation
17
+
18
+ rubyrrdtool requires RRDtool version 1.2 or later. Some RRD functions such
19
+ as rrddump are only available with the latest RRDtool.
20
+
21
+ Installation is standard. If you've installed the gem, you should be ready
22
+ to go. Otherwise, simply run:
23
+
24
+ ruby extconf.rb
25
+ make
26
+ make install
27
+
28
+ I hope this works for you. Please let me know if you have any problems or
29
+ suggestions.
30
+
31
+ Thanks to Tobi for rrdtool!
32
+
33
+
34
+ == To do
35
+
36
+ * Documentation
37
+ * Build an interface that feels more ruby-like around this simple functional
38
+ translation
39
+
40
+
41
+ == License
42
+
43
+ (the MIT license)
44
+
45
+ Copyright (c) 2006
46
+
47
+ Permission is hereby granted, free of charge, to any person obtaining
48
+ a copy of this software and associated documentation files (the
49
+ "Software"), to deal in the Software without restriction, including
50
+ without limitation the rights to use, copy, modify, merge, publish,
51
+ distribute, sublicense, and/or sell copies of the Software, and to
52
+ permit persons to whom the Software is furnished to do so, subject to
53
+ the following conditions:
54
+
55
+ The above copyright notice and this permission notice shall be
56
+ included in all copies or substantial portions of the Software.
57
+
58
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
59
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
60
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
61
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
62
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
63
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
64
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- ruby -*-
3
+
4
+ require 'rubygems'
5
+ require 'hoe'
6
+
7
+ RUBY_RRDTOOL_VERSION = '0.6.0'
8
+
9
+ Hoe.new("RubyRRDtool", RUBY_RRDTOOL_VERSION) do |p|
10
+ p.rubyforge_name = "rubyrrdtool"
11
+ p.summary = "Ruby language binding for RRD tool version 1.2+"
12
+ p.description = p.paragraphs_of('README', 2..3).join("\n\n")
13
+
14
+ p.url = "http://rubyforge.org/projects/rubyrrdtool/"
15
+
16
+ p.author = [ "David Bacher", "Mark Probert" ]
17
+ p.email = "drbacher @nospam@ alum dot mit dot edu"
18
+
19
+ p.spec_extras = { 'extensions' => 'extconf.rb' }
20
+
21
+ p.changes =<<EOC
22
+ * applied patches (thanks to Hrvoje Marjanovic, Akihiro Sagawa and Thorsten von Eicken)
23
+ * changed version, graph, xport methods to be class methods
24
+ * removed dump method from pre two-arg-dump rrdtool versions
25
+ * fixed compiler warnings in debug output
26
+ * hoe-ified Rakefile
27
+ EOC
28
+ end
29
+
30
+ desc "Build RRD driver"
31
+ task :build do
32
+ puts `[ -e Makefile ] || ruby extconf.rb; make`
33
+ end
34
+
35
+ desc "Rebuild RRD driver"
36
+ task :rebuild do
37
+ puts `ruby extconf.rb; make clean all`
38
+ end
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env ruby
2
+ # $Id: function_test.rb,v 1.2 2006/10/18 21:43:38 dbach Exp $
3
+ # Driver does not carry cash.
4
+
5
+ # insert the lib directory at the front of the path to pick up
6
+ # the latest build -- useful for testing during development
7
+ $:.unshift '../lib'
8
+
9
+ require 'RRDtool'
10
+
11
+ name = "test"
12
+ rrdname = "#{name}.rrd"
13
+ start = Time.now.to_i
14
+
15
+ rrd = RRDtool.new(rrdname)
16
+ puts "created new RRD database -> #{rrd.rrdname}"
17
+
18
+
19
+ # ---------------------------------------------
20
+ # Version
21
+ # ---------------------------------------------
22
+ puts "RRD Version "
23
+ n = RRDtool.version
24
+ puts "version() returned #{(n.nil?) ? "nil" : n}"
25
+ puts
26
+
27
+
28
+
29
+
30
+ # ---------------------------------------------
31
+ # Create
32
+ # ---------------------------------------------
33
+ puts "creating #{rrdname}"
34
+ n = rrd.create(
35
+ 300, start-1,
36
+ ["DS:a:GAUGE:600:U:U",
37
+ "DS:b:GAUGE:600:U:U",
38
+ "RRA:AVERAGE:0.5:1:300"])
39
+ puts "create() returned #{(n.nil?) ? "nil" : n}"
40
+ puts
41
+
42
+
43
+ # ---------------------------------------------
44
+ # Update
45
+ # ---------------------------------------------
46
+ puts "updating #{rrdname}"
47
+ val = start.to_i
48
+ while (val < (start.to_i+300*300)) do
49
+ rrd.update("a:b", ["#{val}:#{rand()}:#{Math.sin(val / 3000) * 50 + 50}"])
50
+ val += 300
51
+ end
52
+ puts
53
+
54
+
55
+
56
+
57
+ # ---------------------------------------------
58
+ # Dump
59
+ # ---------------------------------------------
60
+ puts "dumping #{rrdname}"
61
+ n = rrd.dump
62
+ puts "dump() returned #{(n.nil?) ? "nil" : n}"
63
+ puts
64
+
65
+
66
+
67
+ # ---------------------------------------------
68
+ # Fetch
69
+ # ---------------------------------------------
70
+ puts "fetching data from #{rrdname}"
71
+ arr = rrd.fetch(["AVERAGE"])
72
+ data = arr[3]
73
+ puts "got #{data.length} data points"
74
+ puts
75
+
76
+
77
+
78
+ # ---------------------------------------------
79
+ # Info
80
+ # ---------------------------------------------
81
+ puts "RRD info "
82
+ h = rrd.info
83
+ puts "info() returned #{h.size} items"
84
+ h.each do |k, v|
85
+ puts " #{k} --> #{v}"
86
+ end
87
+ puts
88
+
89
+
90
+
91
+ # ---------------------------------------------
92
+ # Graph
93
+ # ---------------------------------------------
94
+ puts "generating graph #{name}.png"
95
+ rrd.graph(
96
+ ["#{name}.png",
97
+ "--title", " RubyRRD Demo",
98
+ "--start", "#{start} + 1 h",
99
+ "--end", "#{start} + 1000 min",
100
+ "--interlace",
101
+ "--imgformat", "PNG",
102
+ "--width=450",
103
+ "DEF:a=#{rrd.rrdname}:a:AVERAGE",
104
+ "DEF:b=#{rrd.rrdname}:b:AVERAGE",
105
+ "CDEF:line=TIME,2400,%,300,LT,a,UNKN,IF",
106
+ "AREA:b#00b6e4:beta",
107
+ "AREA:line#0022e9:alpha",
108
+ "LINE3:line#ff0000"])
109
+ puts
110
+
111
+
112
+ exit # <<<<============== STOP ==================
113
+
114
+ # ---------------------------------------------
115
+ # First
116
+ # ---------------------------------------------
117
+ puts "first value for #{rrdname}"
118
+ n = rrd.first(nil)
119
+ puts "first() returned #{(n.nil?) ? "nil" : n}"
120
+ n = rrd.first(0)
121
+ puts "first(0) returned #{(n.nil?) ? "nil" : n}"
122
+ puts
123
+
124
+ # ---------------------------------------------
125
+ # Last
126
+ # ---------------------------------------------
127
+ puts "last value for #{rrdname}"
128
+ n = rrd.last
129
+ puts "last() returned #{(n.nil?) ? "nil" : n}"
130
+ puts
131
+
132
+
133
+ print "This script has created #{name}.png in the current directory\n";
134
+ print "This demonstrates the use of the TIME and % RPN operators\n";
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "RRDtool"
4
+
5
+ name = "minmax"
6
+ rrdname = "#{name}.rrd"
7
+ start = Time.now.to_i
8
+
9
+ rrd = RRDtool.new(rrdname)
10
+ puts "created new RRD database -> #{rrd.rrdname}"
11
+
12
+ puts "vers -> #{RRDtool.version}"
13
+
14
+ # ---------------------------------------------
15
+ # Create
16
+ # ---------------------------------------------
17
+ puts " .. creating #{rrdname}"
18
+ n = rrd.create(
19
+ 300, start-1,
20
+ ["DS:ds_0:GAUGE:600:U:U",
21
+ "RRA:AVERAGE:0.5:1:300",
22
+ "RRA:MIN:0.5:12:300",
23
+ "RRA:MAX:0.5:12:300"])
24
+
25
+ # ---------------------------------------------
26
+ # Update
27
+ # ---------------------------------------------
28
+ puts " .. updating #{rrdname}"
29
+ val = start.to_i
30
+ while (val < (start.to_i+300*300)) do
31
+ rrd.update("ds_0", ["#{val}:#{Math.sin(val / 3000) * 50 + 50}"])
32
+ val += 300
33
+ # sleep(1)
34
+ end
35
+
36
+
37
+
38
+ # ---------------------------------------------
39
+ # Graph
40
+ # ---------------------------------------------
41
+ puts "generating graph #{name}.png"
42
+ RRDtool.graph(
43
+ ["#{name}.png",
44
+ "DEF:a=#{rrdname}:ds_0:AVERAGE",
45
+ "DEF:b=#{rrdname}:ds_0:MIN",
46
+ "DEF:c=#{rrdname}:ds_0:MAX",
47
+ "--title", " #{name} Demo",
48
+ "--start", "now",
49
+ "--end", "start+1d",
50
+ "--lower-limit=0",
51
+ "--interlace",
52
+ "--imgformat", "PNG",
53
+ "--width=450",
54
+ "AREA:a#00b6e4:real",
55
+ "LINE1:b#0022e9:min",
56
+ "LINE1:c#000000:max"])
57
+
58
+
59
+ puts "This script has created #{rrdname}.png in the current directory"
60
+ puts "This demonstrates the use of the TIME and % RPN operators"
data/extconf.rb ADDED
@@ -0,0 +1,29 @@
1
+ # $Id: extconf.rb,v 1.3 2006/10/17 23:34:06 dbach Exp $
2
+ # Lost ticket pays maximum rate.
3
+
4
+ require 'mkmf'
5
+
6
+ # Be sure to say where you rrdtool lives:
7
+ # ruby ./extconf.rb --with-rrd-dir=/usr/local/rrdtool-1.2.12
8
+ libpaths=%w(/lib /usr/lib /usr/local/lib)
9
+ %w(art_lgpl_2 freetype png z).sort.reverse.each do |lib|
10
+ find_library(lib, nil, *libpaths)
11
+ end
12
+
13
+ dir_config("rrd")
14
+ # rrd_first is only defined rrdtool >= 1.2.x
15
+ have_library("rrd", "rrd_first")
16
+
17
+ # rrd_dump_r has 2nd arg in rrdtool >= 1.2.14
18
+ if try_link(<<EOF)
19
+ #include <rrd.h>
20
+ main()
21
+ {
22
+ rrd_dump_r("/dev/null", NULL);
23
+ }
24
+ EOF
25
+ $CFLAGS += " -DHAVE_RRD_DUMP_R_2"
26
+ end
27
+
28
+ create_makefile("RRDtool")
29
+
data/rrd_addition.h ADDED
@@ -0,0 +1,47 @@
1
+ /* -*- C -*-
2
+ * file: rrd_addition.h
3
+ * date: $Date: 2005/08/04 01:16:07 $
4
+ * init: 2005-07-26
5
+ * vers: $Version$
6
+ * auth: $Author: probertm $
7
+ * -----
8
+ *
9
+ * Support file to add rrd_info() to rrd.h
10
+ *
11
+ */
12
+ #ifndef __RRD_ADDITION_H
13
+ #define __RRD_ADDITION_H
14
+
15
+ #if defined(__cplusplus) || defined(c_plusplus)
16
+ extern "C" {
17
+ #endif
18
+
19
+ /* rrd info interface */
20
+ enum info_type { RD_I_VAL=0,
21
+ RD_I_CNT,
22
+ RD_I_STR,
23
+ RD_I_INT };
24
+
25
+ typedef union infoval {
26
+ unsigned long u_cnt;
27
+ rrd_value_t u_val;
28
+ char *u_str;
29
+ int u_int;
30
+ } infoval;
31
+
32
+ typedef struct info_t {
33
+ char *key;
34
+ enum info_type type;
35
+ union infoval value;
36
+ struct info_t *next;
37
+ } info_t;
38
+
39
+ info_t *rrd_info(int, char **);
40
+ info_t *rrd_info_r(char *);
41
+
42
+
43
+
44
+ #if defined(__cplusplus) || defined(c_plusplus)
45
+ }
46
+ #endif
47
+ #endif /* __RRD_ADDITION_H */
data/rubyrrdtool.c ADDED
@@ -0,0 +1,1093 @@
1
+ /* -----
2
+ * file: rubyrrdtool.c
3
+ * date: $Date: 2006/10/18 21:44:10 $
4
+ * init: 2005-07-26
5
+ * vers: $Version$
6
+ * auth: $Author: dbach $
7
+ * -----
8
+ * This is a Ruby extension (binding) for RRDTool
9
+ *
10
+ * http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/index.en.html
11
+ *
12
+ * It is based on the initial work done by Miles Egan <miles@caddr.com>
13
+ * and modified by Mark Probert (probert at acm dot org) to bring
14
+ * support in line with RRDTool 1.2.x
15
+ *
16
+ * Released under the MIT Licence
17
+ *
18
+ */
19
+
20
+ #include <unistd.h>
21
+ #include <stdbool.h>
22
+ #include <math.h> /* for isnan */
23
+ #include <ruby.h>
24
+ #include <rrd.h>
25
+ #include "rrd_addition.h"
26
+
27
+ /* printf debugging */
28
+ #define R_RRD_DEBUG_OFF 0 /* no debugging */
29
+ #define R_RRD_DEBUG_SIM 1 /* basic debug */
30
+ #define R_RRD_DEBUG_DET 2 /* more details */
31
+ #undef R_RRD_DBG
32
+
33
+ #ifdef R_RRD_DBG
34
+ void _dbug(int level, char *s) {
35
+ if (level <= R_RRD_DBG) {
36
+ printf(" --> %s\n", s);
37
+ }
38
+ }
39
+ #endif
40
+
41
+ VALUE cRRDtool; /* class */
42
+ VALUE rb_eRRDtoolError;
43
+
44
+ extern int optind;
45
+ extern int opterr;
46
+
47
+ typedef int (*RRDtoolFUNC)(int argc, char ** argv);
48
+
49
+ /*
50
+ * define macros for easy error checking
51
+ */
52
+
53
+ #define RRD_RAISE rb_raise(rb_eRRDtoolError, rrd_get_error());
54
+
55
+ #define RRD_CHECK_ERROR if (rrd_test_error()) RRD_RAISE
56
+
57
+ #define NUM_BUF_SZ 80
58
+
59
+ /*
60
+ * Define a structure to store counted array of strings
61
+ */
62
+ typedef struct string_array_t {
63
+ int len;
64
+ char **strs;
65
+ } s_arr;
66
+
67
+ /*
68
+ * Create a new array of strings based on a Ruby array.
69
+ *
70
+ * If name_f value is TRUE, then we need to put the
71
+ * rrdname value as the first element of the array.
72
+ *
73
+ * If dummy_f is true, then add "dummy" at index 0.
74
+ * I have no idea why we do this, but it seems to be
75
+ * needed for some reason.
76
+ *
77
+ */
78
+ static s_arr s_arr_new(VALUE self, bool name_f, bool dummy_f, VALUE strs)
79
+ {
80
+ s_arr a;
81
+ int i, j; /* index counters */
82
+ VALUE rrd; /* name of the RRD we are dealing with */
83
+
84
+ #ifdef R_RRD_DBG
85
+ char buf[NUM_BUF_SZ+1];
86
+ #endif
87
+
88
+ #ifdef R_RRD_DBG
89
+ snprintf(buf, NUM_BUF_SZ, "s_arr: name_f=[%d] dummy_f=[%d]", name_f, dummy_f);
90
+ buf[NUM_BUF_SZ] = 0;
91
+ _dbug(R_RRD_DEBUG_DET, buf);
92
+ #endif
93
+
94
+ rrd = rb_iv_get(self, "@rrdname");
95
+ Check_Type(strs, T_ARRAY);
96
+
97
+ /* set the array length */
98
+ a.len = RARRAY(strs)->len;
99
+ if (name_f) { a.len++; }
100
+ if (dummy_f) { a.len++; }
101
+ a.strs = ALLOC_N(char*, a.len);
102
+
103
+ #ifdef R_RRD_DBG
104
+ snprintf(buf, NUM_BUF_SZ, "s_arr: array length set to %d", a.len);
105
+ buf[NUM_BUF_SZ] = 0;
106
+ _dbug(R_RRD_DEBUG_DET, buf);
107
+ #endif
108
+
109
+ i = 0;
110
+ /* drb: some of the rrd API functions assume the action preceeds the
111
+ * command arguments */
112
+ if (dummy_f) {
113
+ a.strs[i] = strdup("dummy");
114
+ i++;
115
+ }
116
+
117
+ /* add the rrdname if needed */
118
+ if (name_f) {
119
+ a.strs[i] = strdup(STR2CSTR(rrd));
120
+ i++;
121
+ }
122
+
123
+ /* now add the strings */
124
+ j = 0;
125
+ while (i < a.len) {
126
+ VALUE v = rb_ary_entry(strs, j);
127
+
128
+ switch (TYPE(v)) {
129
+ case T_BIGNUM:
130
+ case T_FIXNUM:
131
+ v = rb_obj_as_string(v);
132
+ /* FALLTHROUGH */
133
+ case T_STRING:
134
+ a.strs[i] = strdup(StringValuePtr(v));
135
+ #ifdef R_RRD_DBG
136
+ snprintf(buf, NUM_BUF_SZ, "s_arr: adding: i=%d val=%s", i, a.strs[i]);
137
+ buf[NUM_BUF_SZ] = 0;
138
+ _dbug(R_RRD_DEBUG_DET, buf);
139
+ #endif
140
+ break;
141
+
142
+ default:
143
+ #ifdef R_RRD_DBG
144
+ snprintf(buf, NUM_BUF_SZ, "s_arr: adding: i=%d type=%d", i, TYPE(v));
145
+ buf[NUM_BUF_SZ] = 0;
146
+ _dbug(R_RRD_DEBUG_DET, buf);
147
+ #endif
148
+ rb_raise(rb_eTypeError, "invalid argument for string array");
149
+ break;
150
+ }
151
+ i++; j++;
152
+ }
153
+
154
+ #ifdef R_RRD_DBG
155
+ snprintf(buf, NUM_BUF_SZ, "s_arr: len -> %d", a.len);
156
+ buf[NUM_BUF_SZ] = 0;
157
+ _dbug(R_RRD_DEBUG_SIM, buf);
158
+ for (i = 0; i < a.len; i++) {
159
+ snprintf(buf, NUM_BUF_SZ, "s_arr[%d] -> %s", i, a.strs[i]);
160
+ buf[NUM_BUF_SZ] = 0;
161
+ _dbug(R_RRD_DEBUG_SIM, buf);
162
+ }
163
+ #endif
164
+ return a;
165
+ }
166
+
167
+
168
+ /*
169
+ * clean up the string array
170
+ */
171
+ static void s_arr_del(s_arr a)
172
+ {
173
+ int i;
174
+ for (i = 0; i < a.len; i++) {
175
+ free(a.strs[i]);
176
+ }
177
+ free(a.strs);
178
+ }
179
+
180
+
181
+ /*
182
+ * add an element to a string array at the start of the array
183
+ */
184
+ static bool s_arr_push(char *val, s_arr *sa) {
185
+ char **tmp;
186
+ int i;
187
+ #ifdef R_RRD_DBG
188
+ char buf[NUM_BUF_SZ+1];
189
+ #endif
190
+
191
+ #ifdef R_RRD_DBG
192
+ snprintf(buf, NUM_BUF_SZ, "s_arr_push: n=%d val=%s", sa->len, val);
193
+ buf[NUM_BUF_SZ] = 0;
194
+ _dbug(R_RRD_DEBUG_SIM, buf);
195
+ #endif
196
+
197
+ /* set the array length */
198
+ sa->len += 1;
199
+ tmp = ALLOC_N(char*, sa->len);
200
+
201
+ i = 0;
202
+ tmp[i++] = strdup(val);
203
+ while(i <= sa->len) {
204
+ if (sa->strs[i-1] != NULL) {
205
+ tmp[i] = strdup(sa->strs[i-1]);
206
+ free(sa->strs[i-1]);
207
+ }
208
+ i++;
209
+ }
210
+ sa->strs = tmp;
211
+
212
+ #ifdef R_RRD_DBG
213
+ snprintf(buf, NUM_BUF_SZ, "s_arr_push: len -> %d", sa->len);
214
+ buf[NUM_BUF_SZ] = 0;
215
+ _dbug(R_RRD_DEBUG_SIM, buf);
216
+ for (i = 0; i < sa->len; i++) {
217
+ snprintf(buf, NUM_BUF_SZ, "s_arr_add: s_arr[%d] -> %s", i, sa->strs[i]);
218
+ buf[NUM_BUF_SZ] = 0;
219
+ _dbug(R_RRD_DEBUG_SIM, buf);
220
+ }
221
+ #endif
222
+ return true;
223
+ }
224
+
225
+
226
+ /*
227
+ * reset the internal state
228
+ */
229
+ static void reset_rrd_state()
230
+ {
231
+ optind = 0;
232
+ opterr = 0;
233
+ rrd_clear_error();
234
+ }
235
+
236
+
237
+ /*
238
+ * Document-method: create
239
+ *
240
+ * call-seq:
241
+ * rrd.create(pdp_step, last_up, arg_array) -> [Qnil|Qtrue]
242
+ *
243
+ * The create function of RRDtool lets you set up new Round Robin
244
+ * Database (RRD) files. The file is created at its final, full size
245
+ * and filled with *UNKNOWN* data.
246
+ *
247
+ * create filename [--start|-b start time] [--step|-s step]
248
+ * [DS:ds-name:DST:heartbeat:min:max] [RRA:CF:xff:steps:rows]
249
+ *
250
+ *
251
+ * filename
252
+ * The name of the RRD you want to create. RRD files should end with
253
+ * the extension .rrd. However, RRDtool will accept any filename.
254
+ *
255
+ * pdp_step (default: 300 seconds)
256
+ * Specifies the base interval in seconds with which data will be
257
+ * fed into the RRD.
258
+ *
259
+ * last_up start time (default: now - 10s)
260
+ * Specifies the time in seconds since 1970-01-01 UTC when the first
261
+ * value should be added to the RRD. RRDtool will not accept any data
262
+ * timed before or at the time specified.
263
+ *
264
+ * DS:ds-name:DST:dst arguments
265
+ * A single RRD can accept input from several data sources (DS), for
266
+ * example incoming and outgoing traffic on a specific communication line.
267
+ * With the DS configuration option you must define some basic properties
268
+ * of each data source you want to store in the RRD.
269
+ *
270
+ * ds-name is the name you will use to reference this particular data
271
+ * source from an RRD. A ds-name must be 1 to 19 characters long in
272
+ * the characters [a-zA-Z0-9_].
273
+ *
274
+ * DST defines the Data Source Type. The remaining arguments of a data
275
+ * source entry depend on the data source type. For GAUGE, COUNTER, DERIVE,
276
+ * and ABSOLUTE the format for a data source entry is:
277
+ *
278
+ * DS:ds-name:GAUGE | COUNTER | DERIVE | ABSOLUTE:heartbeat:min:max
279
+ *
280
+ * For COMPUTE data sources, the format is:
281
+ *
282
+ * DS:ds-name:COMPUTE:rpn-expression
283
+ *
284
+ * NB: we use the thread-safe version of the command rrd_create_r()
285
+ */
286
+ VALUE rrdtool_create(VALUE self, VALUE ostep, VALUE update, VALUE args)
287
+ {
288
+ s_arr a; /* varargs in the form of a string array */
289
+ VALUE rval; /* our result */
290
+ VALUE rrd; /* name of the RRD file to create */
291
+ unsigned long pdp_step; /* the stepping time interval */
292
+ time_t last_up; /* last update time */
293
+ #ifdef R_RRD_DBG
294
+ char buf[NUM_BUF_SZ+1];
295
+ #endif
296
+ int result;
297
+
298
+ reset_rrd_state();
299
+
300
+ rrd = rb_iv_get(self, "@rrdname");
301
+
302
+ /* conversion ... */
303
+ pdp_step = NUM2LONG(ostep);
304
+ last_up = (time_t)NUM2LONG(update);
305
+ #ifdef R_RRD_DBG
306
+ snprintf(buf, NUM_BUF_SZ, "n=[%s] : step=%lu : up=%ld", STR2CSTR(rrd),
307
+ pdp_step, (long int)last_up);
308
+ buf[NUM_BUF_SZ] = 0;
309
+ _dbug(R_RRD_DEBUG_SIM, buf);
310
+ #endif
311
+ a = s_arr_new(self, false, false, args);
312
+ #ifdef R_RRD_DBG
313
+ _dbug(R_RRD_DEBUG_SIM, "call");
314
+ #endif
315
+
316
+ /* now run the command */
317
+ result = rrd_create_r(STR2CSTR(rrd), pdp_step, last_up, a.len, a.strs);
318
+
319
+ #ifdef R_RRD_DBG
320
+ _dbug(R_RRD_DEBUG_SIM, "cleanup");
321
+ #endif
322
+ s_arr_del(a);
323
+
324
+ if (result == -1) {
325
+ RRD_RAISE;
326
+ rval = Qnil;
327
+ } else {
328
+ rval = Qtrue;
329
+ }
330
+ return rval;
331
+ }
332
+
333
+
334
+ /*
335
+ * Document-method: dump
336
+ *
337
+ * call-seq:
338
+ * rrd.dump -> [Qnil|Qtrue]
339
+ *
340
+ * The dump function prints the contents of an RRD in human readable (?)
341
+ * XML format. This format can be read by rrd_restore. Together they allow
342
+ * you to transfer your files from one computer architecture to another
343
+ * as well to manipulate the contents of an RRD file in a somewhat more
344
+ * convenient manner.
345
+ *
346
+ * This routine uses th thread-safe version rrd_dump_r()
347
+ *
348
+ * Note: This routine is a bit of a mess from a scripting persepective.
349
+ * rrd_dump_r() doesn't take a filename for the output, it just
350
+ * spits the XML to stdout. Redirection from with an extension
351
+ * seems to be hard. So, for the moment, we just rely on the
352
+ * default behaviour and send in a change request for the fn.
353
+ *
354
+ * For the version that takes
355
+ *
356
+ *
357
+ */
358
+ #ifdef HAVE_RRD_DUMP_R_2
359
+ VALUE rrdtool_dump(VALUE self, VALUE output)
360
+ {
361
+ int ret; /* result of rrd_dump_r */
362
+ VALUE rval; /* our result */
363
+ VALUE rrd; /* rrd database filename */
364
+
365
+ reset_rrd_state();
366
+
367
+ rrd = rb_iv_get(self, "@rrdname");
368
+
369
+ /* type checking */
370
+ Check_Type(output, T_STRING);
371
+
372
+ ret = rrd_dump_r(STR2CSTR(rrd), STR2CSTR(output));
373
+ if (ret == -1) {
374
+ RRD_RAISE;
375
+ rval = Qnil;
376
+ } else {
377
+ rval = Qtrue;
378
+ }
379
+ return rval;
380
+ }
381
+ #endif
382
+
383
+ /*
384
+ * Document-method: first
385
+ *
386
+ * call-seq:
387
+ * rrd.first(rra_idx) -> [Qnil|BIGNUM(time)]
388
+ *
389
+ * The first function returns the UNIX timestamp of the first data sample
390
+ * entered into the specified RRA of the RRD file.
391
+ *
392
+ * The index number of the RRA that is to be examined. If not specified,
393
+ * the index defaults to zero. RRA index numbers can be determined through
394
+ * rrdtool info.
395
+ *
396
+ * NB: this function uses the thread-safe version rrd_first_r()
397
+ *
398
+ */
399
+ VALUE rrdtool_first(VALUE self, VALUE orra_idx)
400
+ {
401
+ VALUE rval; /* our result */
402
+ VALUE rrd; /* rrd database filename */
403
+ int idx; /* index in integer form */
404
+ time_t when; /* the found value */
405
+ #ifdef R_RRD_DBG
406
+ char buf[NUM_BUF_SZ+1];
407
+ #endif
408
+
409
+ reset_rrd_state();
410
+
411
+ rrd = rb_iv_get(self, "@rrdname");
412
+ if (orra_idx == Qnil) { idx = 0; }
413
+ else {
414
+ idx = NUM2INT(orra_idx);
415
+ }
416
+
417
+ when = rrd_first_r(STR2CSTR(rrd), idx);
418
+ if (when == -1) {
419
+ RRD_RAISE;
420
+ rval = Qnil;
421
+ } else {
422
+ rval = LONG2NUM(when);
423
+ }
424
+
425
+ #ifdef R_RRD_DBG
426
+ snprintf(buf, NUM_BUF_SZ, "first: rrd=[%s] : idx=%d : val=%ld",
427
+ STR2CSTR(rrd), idx, when);
428
+ buf[NUM_BUF_SZ] = 0;
429
+ _dbug(R_RRD_DEBUG_SIM, buf);
430
+ #endif
431
+ return rval;
432
+ }
433
+
434
+
435
+ /*
436
+ * Document-method: last
437
+ *
438
+ * call-seq:
439
+ * rrd.last -> [Qnil|BIGNUM(time)]
440
+ *
441
+ * The last function returns the UNIX timestamp of the most recent
442
+ * update of the RRD.
443
+ *
444
+ * NB: this function uses the thread-safe version rrd_lasst_r()
445
+ *
446
+ */
447
+ VALUE rrdtool_last(VALUE self)
448
+ {
449
+ VALUE rval; /* our result */
450
+ VALUE rrd; /* rrd database filename */
451
+ time_t when; /* the found value */
452
+ #ifdef R_RRD_DBG
453
+ char buf[NUM_BUF_SZ+1];
454
+ #endif
455
+
456
+ reset_rrd_state();
457
+
458
+ rrd = rb_iv_get(self, "@rrdname");
459
+
460
+ when = rrd_last_r(STR2CSTR(rrd));
461
+ if (when == -1) {
462
+ RRD_RAISE;
463
+ rval = Qnil;
464
+ } else {
465
+ rval = LONG2NUM(when);
466
+ }
467
+
468
+ #ifdef R_RRD_DBG
469
+ snprintf(buf, NUM_BUF_SZ, "last: rrd=[%s] : val=%ld", STR2CSTR(rrd), when);
470
+ buf[NUM_BUF_SZ] = 0;
471
+ _dbug(R_RRD_DEBUG_SIM, buf);
472
+ #endif
473
+ return rval;
474
+ }
475
+
476
+
477
+ /*
478
+ * Document-method: version
479
+ *
480
+ * call-seq:
481
+ * rrd.version -> [Qnil|FLOAT(version_number)]
482
+ *
483
+ * Returns the version number of the RRDtool library
484
+ *
485
+ */
486
+ VALUE rrdtool_version(VALUE self)
487
+ {
488
+ VALUE rval; /* our result */
489
+ double vers; /* version number */
490
+ #ifdef R_RRD_DBG
491
+ char buf[NUM_BUF_SZ+1];
492
+ #endif
493
+
494
+ reset_rrd_state();
495
+
496
+ vers = rrd_version();
497
+ rval = rb_float_new(vers);
498
+
499
+ #ifdef R_RRD_DBG
500
+ snprintf(buf, NUM_BUF_SZ, "version: val=%f", vers);
501
+ buf[NUM_BUF_SZ] = 0;
502
+ _dbug(R_RRD_DEBUG_SIM, buf);
503
+ #endif
504
+ return rval;
505
+ }
506
+
507
+
508
+
509
+ /*
510
+ * Document-method: update
511
+ *
512
+ * call-seq:
513
+ * rrd.update(template, arg_array) -> [Qnil|Qtrue]
514
+ *
515
+ * The update function feeds new data values into an RRD. The data is time aligned
516
+ * (interpolated) according to the properties of the RRD to which the data is written.
517
+ *
518
+ *
519
+ * filename
520
+ * The name of the RRD you want to update.
521
+ *
522
+ * template
523
+ * By default, the update function expects its data input in the order
524
+ * the data sources are defined in the RRD, excluding any COMPUTE data
525
+ * sources (i.e. if the third data source DST is COMPUTE, the third input
526
+ * value will be mapped to the fourth data source in the RRD and so on).
527
+ * This is not very error resistant, as you might be sending the wrong
528
+ * data into an RRD.
529
+ *
530
+ * The template switch allows you to specify which data sources you are
531
+ * going to update and in which order. If the data sources specified in
532
+ * the template are not available in the RRD file, the update process
533
+ * will abort with an error message.
534
+ *
535
+ * While it appears possible with the template switch to update data sources
536
+ * asynchronously, RRDtool implicitly assigns non-COMPUTE data sources missing
537
+ * from the template the *UNKNOWN* value.
538
+ *
539
+ * Do not specify a value for a COMPUTE DST in the update function. If this
540
+ * is done accidentally (and this can only be done using the template switch),
541
+ * RRDtool will ignore the value specified for the COMPUTE DST.
542
+ *
543
+ * N|timestamp:value[:value...]
544
+ * The data used for updating the RRD was acquired at a certain time. This time
545
+ * can either be defined in seconds since 1970-01-01 or by using the letter 'N',
546
+ * in which case the update time is set to be the current time. Negative time
547
+ * values are subtracted from the current time. An AT_STYLE TIME SPECIFICATION
548
+ * MUST NOT be used.
549
+ *
550
+ * The remaining elements of the argument are DS updates. The order of this
551
+ * list is the same as the order the data sources were defined in the RRA. If
552
+ * there is no data for a certain data-source, the letter U (e.g., N:0.1:U:1)
553
+ * can be specified.
554
+ *
555
+ * The format of the value acquired from the data source is dependent on the
556
+ * data source type chosen. Normally it will be numeric, but the data acquisition
557
+ * modules may impose their very own parsing of this parameter as long as the
558
+ * colon (:) remains the data source value separator.
559
+ *
560
+ * NB: This function uses the thread-safe rrd_update_r() version of the call.
561
+ *
562
+ */
563
+ VALUE rrdtool_update(VALUE self, VALUE otemp, VALUE args)
564
+ {
565
+ s_arr a; /* varargs in the form of a string array */
566
+ VALUE rval; /* our result */
567
+ VALUE rrd; /* name of the RRD file to create */
568
+ VALUE tmpl; /* DS template */
569
+ #ifdef R_RRD_DBG
570
+ char buf[NUM_BUF_SZ+1];
571
+ #endif
572
+ int result;
573
+
574
+ reset_rrd_state();
575
+
576
+ rrd = rb_iv_get(self, "@rrdname");
577
+
578
+ /* initial type checking */
579
+ Check_Type(otemp, T_STRING);
580
+ tmpl = StringValue(otemp);
581
+
582
+ #ifdef R_RRD_DBG
583
+ snprintf(buf, NUM_BUF_SZ, "n=[%s] : tmpl=%s", STR2CSTR(rrd), STR2CSTR(tmpl));
584
+ buf[NUM_BUF_SZ] = 0;
585
+ _dbug(R_RRD_DEBUG_SIM, buf);
586
+ #endif
587
+ a = s_arr_new(self, false, false, args);
588
+
589
+ /* now run the command */
590
+ result = rrd_update_r(STR2CSTR(rrd), STR2CSTR(tmpl), a.len, a.strs);
591
+ /* cleanup */
592
+ s_arr_del(a);
593
+
594
+ if (result == -1) {
595
+ RRD_RAISE;
596
+ rval = Qnil;
597
+ } else {
598
+ rval = Qtrue;
599
+ }
600
+ return rval;
601
+ }
602
+
603
+
604
+
605
+ /*
606
+ * default calling mechanism for those functions that take
607
+ * the old style argc, argv paramters with rrdtool_name as
608
+ * the first paramater
609
+ */
610
+ VALUE rrdtool_call(VALUE self, RRDtoolFUNC fn, VALUE args)
611
+ {
612
+ s_arr a; /* varargs in the form of a string array */
613
+ VALUE rval; /* our result */
614
+ int result;
615
+
616
+ reset_rrd_state();
617
+
618
+ a = s_arr_new(self, true, false, args);
619
+
620
+ /* now run the command */
621
+ result = fn(a.len, a.strs);
622
+
623
+ /* cleanup */
624
+ s_arr_del(a);
625
+
626
+ if (result == -1) {
627
+ RRD_RAISE;
628
+ rval = Qnil;
629
+ } else {
630
+ rval = Qtrue;
631
+ }
632
+ return rval;
633
+ }
634
+
635
+
636
+ /*
637
+ * Document-method: tune
638
+ *
639
+ * call-seq:
640
+ * rrd.tune(varargs) -> [Qnil|Qtrue]
641
+ *
642
+ * The tune option allows you to alter some of the basic configuration
643
+ * values stored in the header area of a Round Robin Database (RRD).
644
+ *
645
+ * One application of the tune function is to relax the validation rules
646
+ * on an RRD. This allows to fill a new RRD with data available in larger
647
+ * intervals than what you would normally want to permit. Be very careful
648
+ * with tune operations for COMPUTE data sources. Setting the min, max,
649
+ * and heartbeat for a COMPUTE data source without changing the data source
650
+ * type to a non-COMPUTE DST WILL corrupt the data source header in the RRD.
651
+ *
652
+ * A second application of the tune function is to set or alter parameters
653
+ * used by the specialized function RRAs for aberrant behavior detection.
654
+ *
655
+ */
656
+ VALUE rrdtool_tune(VALUE self, VALUE args)
657
+ {
658
+ return rrdtool_call(self, rrd_tune, args);
659
+ }
660
+
661
+ /*
662
+ * Document-method: resize
663
+ *
664
+ * call-seq:
665
+ * rrd.resize(varargs) -> [Qnil|Qtrue]
666
+ *
667
+ * The resize function is used to modify the number of rows in an RRA.
668
+ *
669
+ * rra-num
670
+ * the RRA you want to alter.
671
+ *
672
+ * GROW
673
+ * used if you want to add extra rows to an RRA. The extra rows will be
674
+ * inserted as the rows that are oldest.
675
+ *
676
+ * SHRINK
677
+ * used if you want to remove rows from an RRA. The rows that will be
678
+ * removed are the oldest rows.
679
+ *
680
+ * rows
681
+ * the number of rows you want to add or remove.
682
+ *
683
+ */
684
+ VALUE rrdtool_resize(VALUE self, VALUE args)
685
+ {
686
+ return rrdtool_call(self, rrd_resize, args);
687
+ }
688
+
689
+
690
+ /*
691
+ * Document-method: restore
692
+ *
693
+ * call-seq:
694
+ * rrd.restore(varargs) -> [Qnil|Qtrue]
695
+ *
696
+ *
697
+ */
698
+ VALUE rrdtool_restore(VALUE self, VALUE oxml, VALUE orrd, VALUE args)
699
+ {
700
+ s_arr a; /* varargs in the form of a string array */
701
+ VALUE rval; /* our result */
702
+ VALUE rrd; /* name of the RRD file to create */
703
+ VALUE xml; /* XML template */
704
+ #ifdef R_RRD_DBG
705
+ char buf[NUM_BUF_SZ+1];
706
+ #endif
707
+ int result;
708
+
709
+ reset_rrd_state();
710
+
711
+ rrd = rb_iv_get(self, "@rrdname");
712
+
713
+ /* initial type checking */
714
+ Check_Type(oxml, T_STRING);
715
+ xml = StringValue(oxml);
716
+ Check_Type(orrd, T_STRING);
717
+ rrd = StringValue(orrd);
718
+
719
+ #ifdef R_RRD_DBG
720
+ snprintf(buf, NUM_BUF_SZ, "restore: xml=%s rrd=%s",
721
+ STR2CSTR(xml), STR2CSTR(rrd));
722
+ buf[NUM_BUF_SZ] = 0;
723
+ _dbug(R_RRD_DEBUG_SIM, buf);
724
+ #endif
725
+ a = s_arr_new(self, false, false, args);
726
+ s_arr_push(STR2CSTR(rrd), &a);
727
+ s_arr_push(STR2CSTR(xml), &a);
728
+ s_arr_push(STR2CSTR(xml), &a);
729
+
730
+ /* now run the command */
731
+ result = rrd_restore(a.len, a.strs);
732
+
733
+ /* cleanup */
734
+ s_arr_del(a);
735
+
736
+ if (result == -1) {
737
+ RRD_RAISE;
738
+ rval = Qnil;
739
+ } else {
740
+ rval = Qtrue;
741
+ }
742
+ return rval;
743
+ }
744
+
745
+
746
+ /*
747
+ * Document-method: fetch
748
+ *
749
+ * call-seq:
750
+ * rrd.fetch(str_array) -> (start, end, names, data)
751
+ *
752
+ * The fetch function is normally used internally by the graph function to
753
+ * get data from RRDs. fetch will analyze the RRD and try to retrieve the
754
+ * data in the resolution requested. The data fetched is printed to stdout.
755
+ * *UNKNOWN* data is often represented by the string ``NaN'' depending on
756
+ * your OS's printf function.
757
+ *
758
+ * Other call options:
759
+ *
760
+ * --resolution|-r resolution (default is the highest resolution)
761
+ * the interval you want the values to have (seconds per value).
762
+ * rrdfetch will try to match your request, but it will return data
763
+ * even if no absolute match is possible.
764
+ *
765
+ * --start|-s start (default end-1day)
766
+ * start of the time series. A time in seconds since epoch (1970-01-01)
767
+ * is required. Negative numbers are relative to the current time. By
768
+ * default, one day worth of data will be fetched.
769
+ *
770
+ * --end|-e end (default now)
771
+ * the end of the time series in seconds since epoch.
772
+ *
773
+ */
774
+ VALUE rrdtool_fetch(VALUE self, VALUE args)
775
+ {
776
+ s_arr a;
777
+ unsigned long i, j, k, step, ds_cnt;
778
+ rrd_value_t *rrd_data;
779
+ char **ds_names;
780
+ VALUE data, names, rval = Qnil;
781
+ time_t start, end;
782
+ #ifdef R_RRD_DBG
783
+ char buf[NUM_BUF_SZ+1];
784
+ #endif
785
+
786
+ reset_rrd_state();
787
+
788
+ a = s_arr_new(self, true, true, args);
789
+
790
+ rrd_fetch(a.len, a.strs, &start, &end, &step,
791
+ &ds_cnt, &ds_names, &rrd_data);
792
+
793
+ s_arr_del(a);
794
+
795
+ RRD_CHECK_ERROR;
796
+
797
+ /* process the data .. get the names first */
798
+ names = rb_ary_new();
799
+ for (i = 0; i < ds_cnt; i++) {
800
+ rb_ary_push(names, rb_str_new2(ds_names[i]));
801
+ #ifdef R_RRD_DBG
802
+ snprintf(buf, NUM_BUF_SZ, "fetch: names: n=[%s]", ds_names[i]);
803
+ buf[NUM_BUF_SZ] = 0;
804
+ _dbug(R_RRD_DEBUG_SIM, buf);
805
+ #endif
806
+ free(ds_names[i]);
807
+ }
808
+ free(ds_names);
809
+
810
+ /* step over the 2d array containing the data */
811
+ k = 0;
812
+ data = rb_ary_new();
813
+ for (i = start; i <= end; i += step) {
814
+ VALUE line = rb_ary_new2(ds_cnt);
815
+ for (j = 0; j < ds_cnt; j++) {
816
+ rb_ary_store(line, j, rb_float_new(rrd_data[k]));
817
+ k++;
818
+ }
819
+ rb_ary_push(data, line);
820
+ }
821
+ free(rrd_data);
822
+
823
+ /* now prepare an array for ruby to chew on .. */
824
+ rval = rb_ary_new2(4);
825
+ rb_ary_store(rval, 0, LONG2NUM(start));
826
+ rb_ary_store(rval, 1, LONG2NUM(end));
827
+ rb_ary_store(rval, 2, names);
828
+ rb_ary_store(rval, 3, data);
829
+
830
+ return rval;
831
+ }
832
+
833
+
834
+
835
+ /*
836
+ * Document-method: xport
837
+ *
838
+ * call-seq:
839
+ * RRDtool.xport(str_array) -> (start, end, step, col_cnt, legend, data)
840
+ *
841
+ * The xport function's main purpose is to write an XML formatted
842
+ * representation of the data stored in one or several RRDs. It can
843
+ * also extract numerical reports.
844
+ *
845
+ * If no XPORT statements are found, there will be no output.
846
+ *
847
+ * -s|--start seconds (default end-1day)
848
+ * The time when the exported range should begin. Time in
849
+ * seconds since epoch (1970-01-01) is required. Negative
850
+ * numbers are relative to the current time. By default one
851
+ * day worth of data will be printed.
852
+ *
853
+ * -e|--end seconds (default now)
854
+ * The time when the exported range should end. Time in
855
+ * seconds since epoch.
856
+ *
857
+ * -m|--maxrows rows (default 400 rows)
858
+ * This works like the -w|--width parameter of rrdgraph. In
859
+ * fact it is exactly the same, but the parameter was renamed
860
+ * to describe its purpose in this module. See rrdgraph
861
+ * documentation for details.
862
+ *
863
+ * --step value (default automatic)
864
+ * See the rrdgraph manpage documentation.
865
+ *
866
+ * DEF:vname=rrd:ds-name:CF
867
+ * See rrdgraph documentation.
868
+ *
869
+ * CDEF:vname=rpn-expression
870
+ * See rrdgraph documentation.
871
+ *
872
+ * XPORT:vname::legend
873
+ * At least one XPORT statement should be present. The values
874
+ * referenced by vname are printed. Optionally add a legend.
875
+ */
876
+ VALUE rrdtool_xport(VALUE self, VALUE args)
877
+ {
878
+ s_arr a;
879
+ unsigned long i, j, k, step, col_cnt;
880
+ rrd_value_t *rrd_data;
881
+ char **legend_v;
882
+ VALUE data, legends, rval = Qnil;
883
+ time_t start, end;
884
+ #ifdef R_RRD_DBG
885
+ char buf[NUM_BUF_SZ+1];
886
+ #endif
887
+
888
+ reset_rrd_state();
889
+
890
+ a = s_arr_new(self, false, true, args);
891
+
892
+ rrd_xport(a.len, a.strs, 0, &start, &end, &step,
893
+ &col_cnt, &legend_v, &rrd_data);
894
+
895
+ s_arr_del(a);
896
+
897
+ RRD_CHECK_ERROR;
898
+
899
+ /* process the data .. get the legends first */
900
+ legends = rb_ary_new();
901
+ for (i = 0; i < col_cnt; i++) {
902
+ rb_ary_push(legends, rb_str_new2(legend_v[i]));
903
+ #ifdef R_RRD_DBG
904
+ snprintf(buf, NUM_BUF_SZ, "xport: names: n=[%s]", legend_v[i]);
905
+ buf[NUM_BUF_SZ] = 0;
906
+ _dbug(R_RRD_DEBUG_SIM, buf);
907
+ #endif
908
+ free(legend_v[i]);
909
+ }
910
+ free(legend_v);
911
+
912
+ /* step over the 2d array containing the data */
913
+ k = 0;
914
+ data = rb_ary_new();
915
+ for (i = start; i <= end; i += step) {
916
+ VALUE line = rb_ary_new2(col_cnt);
917
+ for (j = 0; j < col_cnt; j++) {
918
+ rb_ary_store(line, j, rb_float_new(rrd_data[k]));
919
+ k++;
920
+ }
921
+ rb_ary_push(data, line);
922
+ }
923
+ free(rrd_data);
924
+
925
+ /* now prepare an array for ruby to chew on .. */
926
+ rval = rb_ary_new2(6);
927
+ rb_ary_store(rval, 0, LONG2NUM(start));
928
+ rb_ary_store(rval, 1, LONG2NUM(end));
929
+ rb_ary_store(rval, 2, UINT2NUM(step));
930
+ rb_ary_store(rval, 3, UINT2NUM(col_cnt));
931
+ rb_ary_store(rval, 4, legends);
932
+ rb_ary_store(rval, 5, data);
933
+
934
+ return rval;
935
+ }
936
+
937
+
938
+
939
+ /*
940
+ * Document-method: graph
941
+ *
942
+ * call-seq:
943
+ * RRDtool.graph(arg_array) -> [Qnil|Qtrue]
944
+ *
945
+ * The graph function generates an image from the data values in an RRD.
946
+ *
947
+ *
948
+ */
949
+ VALUE rrdtool_graph(VALUE self, VALUE args)
950
+ {
951
+ s_arr a;
952
+ char **calcpr, **p;
953
+ VALUE result, print_results;
954
+ int xsize, ysize;
955
+ double ymin, ymax;
956
+
957
+ reset_rrd_state();
958
+
959
+ a = s_arr_new(self, false, true, args);
960
+
961
+ rrd_graph(a.len, a.strs, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
962
+
963
+ s_arr_del(a);
964
+
965
+ RRD_CHECK_ERROR;
966
+
967
+ result = rb_ary_new2(3);
968
+ print_results = rb_ary_new();
969
+ p = calcpr;
970
+ for (p = calcpr; p && *p; p++) {
971
+ rb_ary_push(print_results, rb_str_new2(*p));
972
+ free(*p);
973
+ }
974
+ free(calcpr);
975
+
976
+ rb_ary_store(result, 0, print_results);
977
+ rb_ary_store(result, 1, INT2NUM(xsize));
978
+ rb_ary_store(result, 2, INT2NUM(ysize));
979
+ return result;
980
+ }
981
+
982
+
983
+ /*
984
+ * Document-method: info
985
+ *
986
+ * call-seq:
987
+ * rrd.info -> [Qnil|T_HASH]
988
+ *
989
+ * The info function prints the header information from an RRD in a
990
+ * parsing friendly format.
991
+ *
992
+ */
993
+ VALUE rrdtool_info(VALUE self)
994
+ {
995
+ VALUE rrd; /* rrd database filename */
996
+ VALUE rval; /* our result */
997
+ info_t *data, *p; /* this is what rrd_info()returns */
998
+
999
+ reset_rrd_state();
1000
+
1001
+ rrd = rb_iv_get(self, "@rrdname");
1002
+ data = rrd_info_r(STR2CSTR(rrd));
1003
+
1004
+ RRD_CHECK_ERROR;
1005
+
1006
+ rval = rb_hash_new();
1007
+ while (data) {
1008
+ VALUE key = rb_str_new2(data->key);
1009
+ switch (data->type) {
1010
+ case RD_I_VAL:
1011
+ if (isnan(data->value.u_val)) {
1012
+ rb_hash_aset(rval, key, rb_str_new2("Nil"));
1013
+ }
1014
+ else {
1015
+ rb_hash_aset(rval, key, rb_float_new(data->value.u_val));
1016
+ }
1017
+ break;
1018
+ case RD_I_CNT:
1019
+ rb_hash_aset(rval, key, UINT2NUM(data->value.u_cnt));
1020
+ break;
1021
+ case RD_I_STR:
1022
+ rb_hash_aset(rval, key, rb_str_new2(data->value.u_str));
1023
+ free(data->value.u_str);
1024
+ break;
1025
+ default:
1026
+ rb_hash_aset(rval, key, rb_str_new2("-UNKNOWN-"));
1027
+ }
1028
+ p = data;
1029
+ data = data->next;
1030
+ free(p);
1031
+ }
1032
+ return rval;
1033
+ }
1034
+
1035
+
1036
+ /*
1037
+ * return the rrdname instance variable
1038
+ */
1039
+ static VALUE rrdtool_rrdname(VALUE self) {
1040
+ VALUE rrdname;
1041
+ rrdname = rb_iv_get(self, "@rrdname");
1042
+ return rrdname;
1043
+ }
1044
+
1045
+
1046
+ /*
1047
+ * class initialization makes a context
1048
+ */
1049
+ static VALUE rrdtool_initialize(VALUE self, VALUE ofname) {
1050
+ #ifdef R_RRD_DBG
1051
+ char buf[NUM_BUF_SZ+1];
1052
+ #endif
1053
+ VALUE rrdname;
1054
+
1055
+ rrdname = StringValue(ofname);
1056
+ rb_iv_set(self, "@rrdname", rrdname);
1057
+ #ifdef R_RRD_DBG
1058
+ snprintf(buf, NUM_BUF_SZ, "rrdname=[%s]", STR2CSTR(rrdname));
1059
+ buf[NUM_BUF_SZ] = 0;
1060
+ _dbug(R_RRD_DEBUG_SIM, buf);
1061
+ #endif
1062
+ return self;
1063
+ }
1064
+
1065
+
1066
+ /*
1067
+ * Define and create the Ruby objects and methods
1068
+ */
1069
+ void Init_RRDtool()
1070
+ {
1071
+ cRRDtool = rb_define_class("RRDtool", rb_cObject);
1072
+
1073
+ rb_eRRDtoolError = rb_define_class("RRDtoolError", rb_eStandardError);
1074
+
1075
+ rb_define_method(cRRDtool, "initialize", rrdtool_initialize, 1);
1076
+ rb_define_method(cRRDtool, "rrdname", rrdtool_rrdname, 0);
1077
+ rb_define_method(cRRDtool, "create", rrdtool_create, 3);
1078
+ rb_define_method(cRRDtool, "update", rrdtool_update, 2);
1079
+ rb_define_method(cRRDtool, "fetch", rrdtool_fetch, 1);
1080
+ rb_define_method(cRRDtool, "restore", rrdtool_restore, 3);
1081
+ rb_define_method(cRRDtool, "tune", rrdtool_tune, 1);
1082
+ rb_define_method(cRRDtool, "last", rrdtool_last, 0);
1083
+ rb_define_method(cRRDtool, "first", rrdtool_first, 1);
1084
+ rb_define_method(cRRDtool, "resize", rrdtool_resize, 1);
1085
+ rb_define_method(cRRDtool, "info", rrdtool_info, 0);
1086
+ #ifdef HAVE_RRD_DUMP_R_2
1087
+ rb_define_method(cRRDtool, "dump", rrdtool_dump, 1);
1088
+ #endif
1089
+ /* version() is really a library function */
1090
+ rb_define_singleton_method(cRRDtool, "version", rrdtool_version, 0);
1091
+ rb_define_singleton_method(cRRDtool, "graph", rrdtool_graph, 1);
1092
+ rb_define_singleton_method(cRRDtool, "xport", rrdtool_xport, 1);
1093
+ }