RubyRRDtool 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }