errand 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ Makefile
2
+ test.png
3
+ test.rrd
4
+ mkmf.log
5
+ pkg
@@ -0,0 +1,10 @@
1
+ Makefile
2
+ test.png
3
+ test.rrd
4
+ mkmf.log
5
+ pkg
6
+ *~
7
+ *.gem
8
+ *.o
9
+ *.so
10
+ errand.gemspec
data/AUTHORS ADDED
@@ -0,0 +1,18 @@
1
+ Based on work of
2
+ ----------------
3
+ Miles Egan <miles at caddr.com>
4
+
5
+
6
+ RubyRRDtool authors + contributors
7
+ ----------------------------------
8
+ David Bacher <drbacher at alum.mit.edu>
9
+ Mark Probert <probertm at acm.org>
10
+
11
+ Eric Lindvall (http://github.com/eric)
12
+ Michael Gebetsroither (http://github.com/gebi)
13
+ schmurfy (http://github.com/schmurfy/)
14
+
15
+
16
+ Errand is:
17
+ ----------
18
+ Lindsay Holmwood <lindsay@holmwood.id.au>
@@ -0,0 +1,94 @@
1
+ Errand
2
+ ======
3
+
4
+ Errand provides Ruby bindings for RRD functions (via librrd), and a clear
5
+ API for interacting with RRDs.
6
+
7
+ Check under spec/ for usage examples.
8
+
9
+ Using
10
+ =====
11
+
12
+ To set up an RRD to work with (whether it exists or otherwise):
13
+
14
+ @rrd = Errand.new(:filename => "data.rrd")
15
+
16
+ To create an RRD:
17
+
18
+ @rrd.create(:sources => [
19
+ {:name => "Counter", :type => :counter, :heartbeat => 1800, :min => 0, :max => 4294967295}],
20
+ :archives => [
21
+ {:function => :average, :xff => 0.5, :steps => 1, :rows => 2400}])
22
+
23
+ To update said RRD:
24
+
25
+ @rrd.update(:sources => [{:name => "Counter", :value => 1}]
26
+
27
+ To fetch that data:
28
+
29
+ @rrd.fetch # <= {:start => Time, :end => Time,
30
+ :data => {"Counter" => [nil, nil, nil, 1]}
31
+
32
+ Dependencies
33
+ ============
34
+
35
+ **Errand** requires RRDtool version 1.2 or later. Some RRD functions such
36
+ as rrddump are only available with the latest RRDtool.
37
+
38
+ Installation is standard. If you've installed the gem, you should be ready
39
+ to go.
40
+
41
+ Otherwise, simply run:
42
+
43
+ ruby extconf.rb
44
+ make
45
+ make install
46
+
47
+ This should build a library named `rrd.so` in the current directory. If it
48
+ doesn't, please report bugs at [http://github.com/eric/rubyrrdtool/issues](http://github.com/eric/rubyrrdtool/issues)!
49
+
50
+ Building gem
51
+ ============
52
+
53
+ gem build errand.gemspec
54
+
55
+ Testing
56
+ =======
57
+
58
+ Testing is done with RSpec.
59
+
60
+ To run tests:
61
+
62
+ rake spec
63
+
64
+ Todo
65
+ ====
66
+
67
+ * Extend documentation with examples
68
+
69
+
70
+ License
71
+ =======
72
+
73
+ (the MIT license)
74
+
75
+ Copyright (c) 2006
76
+
77
+ Permission is hereby granted, free of charge, to any person obtaining
78
+ a copy of this software and associated documentation files (the
79
+ "Software"), to deal in the Software without restriction, including
80
+ without limitation the rights to use, copy, modify, merge, publish,
81
+ distribute, sublicense, and/or sell copies of the Software, and to
82
+ permit persons to whom the Software is furnished to do so, subject to
83
+ the following conditions:
84
+
85
+ The above copyright notice and this permission notice shall be
86
+ included in all copies or substantial portions of the Software.
87
+
88
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
89
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
90
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
91
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
92
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
93
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
94
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |gemspec|
6
+ gemspec.name = "errand"
7
+ gemspec.summary = "Ruby language binding for RRD tool version 1.2+"
8
+ gemspec.description = "Errand provides Ruby bindings for RRD functions (via librrd), and a concise DSL for interacting with RRDs."
9
+ gemspec.email = "lindsay@holmwood.id.au"
10
+ gemspec.homepage = "http://auxesis.github.com/errand"
11
+ gemspec.authors = ["Lindsay Holmwood"]
12
+ gemspec.extensions = ["extconf.rb"]
13
+ end
14
+ rescue LoadError
15
+ puts "Jeweler not available. Install it with: gem install jeweler"
16
+ end
17
+
18
+ begin
19
+ require 'spec/rake/spectask'
20
+
21
+ Spec::Rake::SpecTask.new do |t|
22
+ t.spec_opts = ["--options", "spec/spec.opts"]
23
+ end
24
+ rescue LoadError
25
+ puts "RSpec not available. Install it with: gem install rspec"
26
+ end
27
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.7.0
@@ -0,0 +1,30 @@
1
+ # $Id: extconf.rb,v 1.4 2008/03/11 22:47:06 dbach Exp $
2
+
3
+ require 'mkmf'
4
+
5
+ # If there are build errors, be sure to say where your rrdtool lives:
6
+ #
7
+ # ruby ./extconf.rb --with-rrd-dir=/usr/local/rrdtool-1.2.12
8
+
9
+ libpaths=%w(/lib /usr/lib /usr/local/lib /sw/lib /opt/local/lib)
10
+ %w(art_lgpl_2 freetype png z).sort.reverse.each do |lib|
11
+ find_library(lib, nil, *libpaths)
12
+ end
13
+
14
+ dir_config("rrd")
15
+ # rrd_first is only defined rrdtool >= 1.2.x
16
+ have_library("rrd", "rrd_first")
17
+
18
+ # rrd_dump_r has 2nd arg in rrdtool >= 1.2.14
19
+ if try_link(<<EOF)
20
+ #include <rrd.h>
21
+ main()
22
+ {
23
+ rrd_dump_r("/dev/null", NULL);
24
+ }
25
+ EOF
26
+ $CFLAGS += " -DHAVE_RRD_DUMP_R_2"
27
+ end
28
+
29
+ create_makefile("errand_backend")
30
+
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.expand_path(File.join(File.dirname(__FILE__), '..'))
4
+ require 'errand_backend'
5
+
6
+ class Errand
7
+ def initialize(opts={})
8
+ raise ArgumentError unless opts[:filename]
9
+ @filename = opts[:filename]
10
+ @backend = ::ErrandBackend
11
+ end
12
+
13
+ def dump(opts={})
14
+ output = opts[:filename] || ""
15
+ @backend.dump(@filename, output)
16
+ end
17
+
18
+ def last
19
+ @backend.last(@filename)
20
+ end
21
+
22
+ def fetch(opts={})
23
+ start = (opts[:start] || Time.now.to_i - 3600).to_s
24
+ finish = (opts[:finish] || Time.now.to_i).to_s
25
+ function = opts[:function] ? opts[:function].to_s.upcase : "AVERAGE"
26
+
27
+ args = [@filename, "--start", start, "--end", finish, function]
28
+
29
+ data = @backend.fetch(*args)
30
+ start = data[0]
31
+ finish = data[1]
32
+ labels = data[2]
33
+ values = data[3]
34
+ points = {}
35
+
36
+ # compose a saner representation of the data
37
+ labels.each_with_index do |label, index|
38
+ points[label] = []
39
+ values.each do |tuple|
40
+ value = tuple[index].nan? ? nil : tuple[index]
41
+ points[label] << value
42
+ end
43
+ end
44
+
45
+ {:start => start, :finish => finish, :data => points}
46
+ end
47
+
48
+ def create(opts={})
49
+ step = (opts[:step] || 300).to_s
50
+ start = (opts[:start] || Time.now.to_i - 10).to_s
51
+
52
+ options = ["--step", step, "--start", start]
53
+
54
+ sources = opts[:sources].map { |source|
55
+ name = source[:name]
56
+ type = source[:type].to_s.upcase
57
+ heartbeat = source[:heartbeat]
58
+ min = source[:min]
59
+ max = source[:max]
60
+
61
+ ds = ["DS", name, type]
62
+ ds += [heartbeat, min, max] if heartbeat && min && max
63
+
64
+ ds.join(':')
65
+ }
66
+
67
+ archives = opts[:archives].map { |archive|
68
+ function = archive[:function].to_s.upcase
69
+ xff = archive[:xff]
70
+ steps = archive[:steps]
71
+ rows = archive[:rows]
72
+
73
+ rra = ["RRA", function]
74
+ rra += [xff, steps, rows] if xff && steps && rows
75
+
76
+ rra.join(':')
77
+ }
78
+
79
+ args = options + sources + archives
80
+ @backend.create(@filename, *args) # * "flattens" the array
81
+
82
+ true
83
+ end
84
+
85
+ def update(opts={})
86
+ time_specified = opts[:sources].find { |source| source[:time] }
87
+ times = opts[:sources].map {|s| s[:time]}.sort.uniq if time_specified
88
+
89
+ sources = opts[:sources].map { |source|
90
+ source[:name]
91
+ }.join(':')
92
+
93
+ case
94
+ when time_specified && times.size == 1
95
+ values = "#{times.first}:" + opts[:sources].map { |s|
96
+ s[:value]
97
+ }.join(':')
98
+
99
+ args = ["--template", sources, values]
100
+ @backend.update(@filename, *args)
101
+ when time_specified && times.size > 1
102
+ times.each do |t|
103
+ points = opts[:sources].find_all { |source| source[:time] == t }
104
+
105
+ sources = points.map { |p|
106
+ p[:name]
107
+ }.join(':')
108
+
109
+ values = "#{t}:" + points.map { |p|
110
+ p[:value]
111
+ }.join(':')
112
+
113
+ args = ["--template", sources, values]
114
+ @backend.update(@filename, *args)
115
+ end
116
+
117
+ when !time_specified
118
+ values = "N:" + opts[:sources].map { |source|
119
+ source[:value]
120
+ }.join(':')
121
+
122
+ args = ["--template", sources, values]
123
+ @backend.update(@filename, *args)
124
+ end
125
+
126
+ true
127
+ end
128
+
129
+ def info
130
+ @backend.info(@filename)
131
+ end
132
+
133
+ # ordered array of data sources as defined in rrd
134
+ def data_sources
135
+ self.info.keys.grep(/^ds\[/).map { |ds| ds[3..-1].split(']').first}.uniq
136
+ end
137
+
138
+ end
@@ -0,0 +1,75 @@
1
+ #
2
+ # ideas for a graphing DSL
3
+ #
4
+
5
+ module RRDtool
6
+
7
+ # Simple Ruby object to generate the RRD graph parameters
8
+ #
9
+ class Graph
10
+
11
+ # Graph attributes can appear in any order
12
+ attr :image_file, :image_format
13
+ attr :title
14
+ attr :vertical_label
15
+
16
+ attr :width, :height
17
+ attr :start, :duration, :step
18
+ attr :upper_limit, :lower_limit
19
+
20
+ # LATER: there are lots of other RRDtool graph attributes
21
+
22
+ def initialize
23
+ @graph_command = []
24
+ end
25
+
26
+ #
27
+ # The graph commands must be preserved in order
28
+ #
29
+
30
+ # Create a DEF
31
+ def fetch(name, rrd_file, value, type)
32
+ # format: DEF:name=rrd_file:value:type
33
+ @graph_command << "DEF:#{name}=#{rrd_file}:#{value}:#{type}"
34
+ end
35
+
36
+ # Create a CDEF
37
+ def compute(name, expr)
38
+ # format: CDEF:name=expr
39
+ @graph_command << "CDEF:#{name}=#{expr}"
40
+ end
41
+
42
+ # Create GPRINT
43
+ def gprint(name, type, print_expr)
44
+ # format: GPRINTF:name:type:print_expr
45
+ @graph_command << "GPRINTF:#{name}:#{type}:#{print_expr}"
46
+ end
47
+
48
+ # Create a LINE(1,2,...)
49
+ def line(name, width, color, label)
50
+ # format: LINEwidth:name#color:label
51
+ @graph_command << "LINE#{width}:#{name}##{color}:#{label}"
52
+ end
53
+
54
+ # Create a AREA
55
+ def area(name, color)
56
+ # format: AREA:name#color
57
+ @graph_command << "AREA:#{name}##{color}"
58
+ end
59
+
60
+ # Create a STACK
61
+ def stack(name, color)
62
+ # format: STACK:name#color
63
+ @graph_command << "STACK:#{name}##{color}"
64
+ end
65
+
66
+ # Turn the graph definition into a real image
67
+ def publish
68
+ # LATER: add in the graph attributes --
69
+ # i.e., turn the graph attribute hash into an array of [ '--key', 'value', ...]
70
+ RRDtool.graph @graph_command
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -0,0 +1,47 @@
1
+ /* -*- C -*-
2
+ * file: rrd_info.h
3
+ * date: $Date: 2008/03/11 22:47:06 $
4
+ * init: 2005-07-26
5
+ * vers: $Version$
6
+ * auth: $Author: dbach $
7
+ * -----
8
+ *
9
+ * Support file to add rrd_info() to rrd.h
10
+ *
11
+ */
12
+ #ifndef __RRD_INFO_H
13
+ #define __RRD_INFO_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
+
26
+ typedef union infoval {
27
+ unsigned long u_cnt;
28
+ rrd_value_t u_val;
29
+ char *u_str;
30
+ int u_int;
31
+ } infoval;
32
+
33
+ typedef struct info_t {
34
+ char *key;
35
+ rrd_info_type_t type;
36
+ union infoval value;
37
+ struct info_t *next;
38
+ } info_t;
39
+
40
+ //info_t *rrd_info(int, char **);
41
+
42
+
43
+
44
+ #if defined(__cplusplus) || defined(c_plusplus)
45
+ }
46
+ #endif
47
+ #endif /* __RRD_INFO_H */
@@ -0,0 +1,260 @@
1
+ /* $Id: rrdtool_main.c,v 1.1 2008/03/11 22:47:06 dbach Exp $
2
+ * Substantial penalty for early withdrawal.
3
+ */
4
+
5
+ #include <unistd.h>
6
+ #include <math.h> /* for isnan */
7
+ #include <ruby.h>
8
+ #include <rrd.h>
9
+ #include "rrd_info.h"
10
+
11
+ typedef struct string_arr_t {
12
+ int len;
13
+ char **strings;
14
+ } string_arr;
15
+
16
+ VALUE mRRD;
17
+ VALUE rb_eRRDError;
18
+
19
+ typedef int (*RRDFUNC)(int argc, char ** argv);
20
+ #define RRD_CHECK_ERROR \
21
+ if (rrd_test_error()) \
22
+ rb_raise(rb_eRRDError, rrd_get_error()); \
23
+ rrd_clear_error();
24
+
25
+ string_arr string_arr_new(VALUE rb_strings)
26
+ {
27
+ string_arr a;
28
+ char buf[64];
29
+ int i;
30
+
31
+ Check_Type(rb_strings, T_ARRAY);
32
+ a.len = RARRAY_LEN(rb_strings) + 1;
33
+
34
+ a.strings = malloc(a.len * sizeof(char *));
35
+ a.strings[0] = "dummy"; /* first element is a dummy element */
36
+
37
+ for (i = 0; i < a.len - 1; i++) {
38
+ VALUE v = rb_ary_entry(rb_strings, i);
39
+ switch (TYPE(v)) {
40
+ case T_STRING:
41
+ a.strings[i + 1] = strdup(STR2CSTR(v));
42
+ break;
43
+ case T_FIXNUM:
44
+ snprintf(buf, 63, "%d", FIX2INT(v));
45
+ a.strings[i + 1] = strdup(buf);
46
+ break;
47
+ default:
48
+ rb_raise(rb_eTypeError, "invalid argument - %s, expected T_STRING or T_FIXNUM on index %d", rb_class2name(CLASS_OF(v)), i);
49
+ break;
50
+ }
51
+ }
52
+
53
+ return a;
54
+ }
55
+
56
+ void string_arr_delete(string_arr a)
57
+ {
58
+ int i;
59
+
60
+ /* skip dummy first entry */
61
+ for (i = 1; i < a.len; i++) {
62
+ free(a.strings[i]);
63
+ }
64
+
65
+ free(a.strings);
66
+ }
67
+
68
+ void reset_rrd_state()
69
+ {
70
+ optind = 0;
71
+ opterr = 0;
72
+ rrd_clear_error();
73
+ }
74
+
75
+ VALUE rrd_call(RRDFUNC func, VALUE args)
76
+ {
77
+ string_arr a;
78
+
79
+ a = string_arr_new(args);
80
+ reset_rrd_state();
81
+ func(a.len, a.strings);
82
+ string_arr_delete(a);
83
+
84
+ RRD_CHECK_ERROR
85
+
86
+ return Qnil;
87
+ }
88
+
89
+ VALUE rb_rrd_create(VALUE self, VALUE args)
90
+ {
91
+ return rrd_call(rrd_create, args);
92
+ }
93
+
94
+ VALUE rb_rrd_dump(VALUE self, VALUE args)
95
+ {
96
+ return rrd_call(rrd_dump, args);
97
+ }
98
+
99
+ VALUE rb_rrd_fetch(VALUE self, VALUE args)
100
+ {
101
+ string_arr a;
102
+ unsigned long i, j, k, step, ds_cnt;
103
+ rrd_value_t *raw_data;
104
+ char **raw_names;
105
+ VALUE data, names, result;
106
+ time_t start, end;
107
+
108
+ a = string_arr_new(args);
109
+ reset_rrd_state();
110
+ rrd_fetch(a.len, a.strings, &start, &end, &step, &ds_cnt, &raw_names, &raw_data);
111
+ string_arr_delete(a);
112
+
113
+ RRD_CHECK_ERROR
114
+
115
+ names = rb_ary_new();
116
+ for (i = 0; i < ds_cnt; i++) {
117
+ rb_ary_push(names, rb_str_new2(raw_names[i]));
118
+ free(raw_names[i]);
119
+ }
120
+ free(raw_names);
121
+
122
+ k = 0;
123
+ data = rb_ary_new();
124
+ for (i = start; i < end; i += step) {
125
+ VALUE line = rb_ary_new2(ds_cnt);
126
+ for (j = 0; j < ds_cnt; j++) {
127
+ rb_ary_store(line, j, rb_float_new(raw_data[k]));
128
+ k++;
129
+ }
130
+ rb_ary_push(data, line);
131
+ }
132
+ free(raw_data);
133
+
134
+ result = rb_ary_new2(4);
135
+ rb_ary_store(result, 0, INT2NUM(start));
136
+ rb_ary_store(result, 1, INT2NUM(end));
137
+ rb_ary_store(result, 2, names);
138
+ rb_ary_store(result, 3, data);
139
+ return result;
140
+ }
141
+
142
+ VALUE rb_rrd_graph(VALUE self, VALUE args)
143
+ {
144
+ string_arr a;
145
+ char **calcpr, **p;
146
+ VALUE result, print_results;
147
+ int xsize, ysize;
148
+ double ymin, ymax;
149
+
150
+ a = string_arr_new(args);
151
+ reset_rrd_state();
152
+ rrd_graph(a.len, a.strings, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
153
+ string_arr_delete(a);
154
+
155
+ RRD_CHECK_ERROR
156
+
157
+ result = rb_ary_new2(3);
158
+ print_results = rb_ary_new();
159
+ p = calcpr;
160
+ for (p = calcpr; p && *p; p++) {
161
+ rb_ary_push(print_results, rb_str_new2(*p));
162
+ free(*p);
163
+ }
164
+ free(calcpr);
165
+ rb_ary_store(result, 0, print_results);
166
+ rb_ary_store(result, 1, INT2FIX(xsize));
167
+ rb_ary_store(result, 2, INT2FIX(ysize));
168
+ return result;
169
+ }
170
+
171
+ VALUE rb_rrd_info(VALUE self, VALUE args)
172
+ {
173
+ string_arr a;
174
+ info_t *p, *data;
175
+ VALUE result;
176
+
177
+ a = string_arr_new(args);
178
+ data = rrd_info(a.len, a.strings);
179
+ string_arr_delete(a);
180
+
181
+ RRD_CHECK_ERROR
182
+
183
+ result = rb_hash_new();
184
+ while (data) {
185
+ VALUE key = rb_str_new2(data->key);
186
+ switch (data->type) {
187
+ case RD_I_VAL:
188
+ if (isnan(data->value.u_val)) {
189
+ rb_hash_aset(result, key, Qnil);
190
+ }
191
+ else {
192
+ rb_hash_aset(result, key, rb_float_new(data->value.u_val));
193
+ }
194
+ break;
195
+ case RD_I_CNT:
196
+ rb_hash_aset(result, key, INT2FIX(data->value.u_cnt));
197
+ break;
198
+ case RD_I_STR:
199
+ rb_hash_aset(result, key, rb_str_new2(data->value.u_str));
200
+ free(data->value.u_str);
201
+ break;
202
+ }
203
+ p = data;
204
+ data = data->next;
205
+ free(p);
206
+ }
207
+ return result;
208
+ }
209
+
210
+ VALUE rb_rrd_last(VALUE self, VALUE args)
211
+ {
212
+ string_arr a;
213
+ time_t last;
214
+
215
+ a = string_arr_new(args);
216
+ reset_rrd_state();
217
+ last = rrd_last(a.len, a.strings);
218
+ string_arr_delete(a);
219
+
220
+ RRD_CHECK_ERROR
221
+
222
+ return rb_funcall(rb_cTime, rb_intern("at"), 1, INT2FIX(last));
223
+ }
224
+
225
+ VALUE rb_rrd_resize(VALUE self, VALUE args)
226
+ {
227
+ return rrd_call(rrd_resize, args);
228
+ }
229
+
230
+ VALUE rb_rrd_restore(VALUE self, VALUE args)
231
+ {
232
+ return rrd_call(rrd_restore, args);
233
+ }
234
+
235
+ VALUE rb_rrd_tune(VALUE self, VALUE args)
236
+ {
237
+ return rrd_call(rrd_tune, args);
238
+ }
239
+
240
+ VALUE rb_rrd_update(VALUE self, VALUE args)
241
+ {
242
+ return rrd_call(rrd_update, args);
243
+ }
244
+
245
+ void Init_errand_backend()
246
+ {
247
+ mRRD = rb_define_module("ErrandBackend");
248
+ rb_eRRDError = rb_define_class("ErrandError", rb_eStandardError);
249
+
250
+ rb_define_module_function(mRRD, "create", rb_rrd_create, -2);
251
+ rb_define_module_function(mRRD, "dump", rb_rrd_dump, -2);
252
+ rb_define_module_function(mRRD, "fetch", rb_rrd_fetch, -2);
253
+ rb_define_module_function(mRRD, "graph", rb_rrd_graph, -2);
254
+ rb_define_module_function(mRRD, "last", rb_rrd_last, -2);
255
+ rb_define_module_function(mRRD, "resize", rb_rrd_resize, -2);
256
+ rb_define_module_function(mRRD, "restore", rb_rrd_restore, -2);
257
+ rb_define_module_function(mRRD, "tune", rb_rrd_tune, -2);
258
+ rb_define_module_function(mRRD, "update", rb_rrd_update, -2);
259
+ rb_define_module_function(mRRD, "info", rb_rrd_info, -2);
260
+ }
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.expand_path(File.join(File.dirname(__FILE__), '..'))
4
+ require 'lib/errand'
5
+ require 'tmpdir'
6
+
7
+ describe Errand do
8
+
9
+ before(:all) do
10
+ @tmpdir = Dir.mktmpdir
11
+ end
12
+
13
+ before(:each) do
14
+ @rrd = Errand.new(:filename => File.join(@tmpdir, 'test.rrd'))
15
+ end
16
+
17
+ it "should create data" do
18
+
19
+ @rrd.create(:start => Time.now.to_i - 1, :step => 300,
20
+ :sources => [
21
+ {:name => "Counter", :type => :counter, :heartbeat => 1800, :min => 0, :max => 4294967295},
22
+ {:name => "Total", :type => :derive, :heartbeat => 1800, :min => 0, :max => 'U'} ],
23
+ :archives => [
24
+ {:function => :average, :xff => 0.5, :steps => 1, :rows => 2400}]).should be_true
25
+
26
+ sources = @rrd.info.keys.grep(/^ds\[/).map { |ds| ds[3..-1].split(']').first}.uniq
27
+ sources.size.should == 2
28
+
29
+ end
30
+
31
+ it "should dump data" do
32
+ @rrd.create(:start => Time.now.to_i - 1, :step => 300,
33
+ :sources => [
34
+ {:name => "Counter", :type => :counter, :heartbeat => 1800, :min => 0, :max => 4294967295},
35
+ {:name => "Total", :type => :derive, :heartbeat => 1800, :min => 0, :max => 'U'} ],
36
+ :archives => [
37
+ {:function => :average, :xff => 0.5, :steps => 1, :rows => 2400}]).should be_true
38
+
39
+ tmpfile = File.join(@tmpdir, 'test.out')
40
+ lambda {
41
+ @rrd.dump(:filename => tmpfile)
42
+ @rrd.dump # <= no args
43
+ }.should_not raise_error
44
+ end
45
+
46
+ it "should update rrds" do
47
+ @rrd.create(:start => Time.now.to_i - 1, :step => 1,
48
+ :sources => [
49
+ {:name => "Sum", :type => :gauge, :heartbeat => 1800, :min => 0, :max => 4294967295},
50
+ {:name => "Total", :type => :gauge, :heartbeat => 1800, :min => 0, :max => 'U'} ],
51
+ :archives => [
52
+ {:function => :average, :xff => 0.5, :steps => 1, :rows => 2400}]).should be_true
53
+
54
+ @rrd.update(:sources => [
55
+ {:name => "Sum", :value => 1},
56
+ {:name => "Total", :value => 30}]).should be_true
57
+
58
+ @rrd.fetch[:data].each_pair do |source, values|
59
+ values.compact.size.should > 0
60
+ end
61
+ end
62
+
63
+ it "should update with weird data" do
64
+ time = Time.now.to_i - 300
65
+ @rrd.create(:start => time - 1, :step => 1,
66
+ :sources => [
67
+ {:name => "Sum", :type => :gauge, :heartbeat => 1800, :min => 0, :max => 4294967295},
68
+ {:name => "Total", :type => :gauge, :heartbeat => 1800, :min => 0, :max => 'U'} ],
69
+ :archives => [
70
+ {:function => :average, :xff => 0.5, :steps => 1, :rows => 2400}]).should be_true
71
+
72
+ updates = []
73
+ 100.times do |i|
74
+ updates << {:name => "Sum", :time => time + i , :value => 1}
75
+ updates << {:name => "Total", :time => time + i * 4, :value => 30}
76
+ end
77
+
78
+ @rrd.update(:sources => updates)
79
+
80
+ @rrd.fetch[:data].each_pair do |source, values|
81
+ values.compact.size.should > 0
82
+ end
83
+ end
84
+
85
+ it "should fetch data" do
86
+ time = Time.now.to_i - 300
87
+ @rrd.create(:start => time - 1, :step => 1,
88
+ :sources => [
89
+ {:name => "Sum", :type => :gauge, :heartbeat => 1800, :min => 0, :max => 4294967295},
90
+ {:name => "Total", :type => :gauge, :heartbeat => 1800, :min => 0, :max => 'U'} ],
91
+ :archives => [
92
+ {:function => :average, :xff => 0.5, :steps => 1, :rows => 2400}]).should be_true
93
+
94
+ 100.times do |i|
95
+ @rrd.update(:sources => [
96
+ {:name => "Sum", :time => time + i, :value => 1},
97
+ {:name => "Total", :time => time + i, :value => 30}]).should be_true
98
+ end
99
+
100
+ @rrd.fetch[:data].each_pair do |source, values|
101
+ values.compact.size.should > 0
102
+ end
103
+ end
104
+
105
+ it "should return timestamp of first value"
106
+ it "should return timestamp of last value"
107
+
108
+ end
@@ -0,0 +1,4 @@
1
+ --color
2
+ --format
3
+ progress
4
+ --diff
@@ -0,0 +1,172 @@
1
+ # Code Generated by ZenTest v. 3.2.0
2
+ # classname: asrt / meth = ratio%
3
+
4
+ $:.unshift '..'
5
+
6
+ require 'test/unit' unless defined? $ZENTEST and $ZENTEST
7
+ require 'rrd'
8
+
9
+ class TestRRDtool < Test::Unit::TestCase
10
+ def setup
11
+ @f = 'test.rrd'
12
+ @g = (File.basename @f, '.rrd') + '.png'
13
+ File.delete @f if File.exists? @f
14
+ File.delete @g if File.exists? @g
15
+ @r = RRDtool.new @f
16
+ end
17
+
18
+ def teardown
19
+ File.delete @f if File.exists? @f
20
+ # note: leave the graph file around to check it by hand
21
+ @r = nil
22
+ end
23
+
24
+ def create_file
25
+ @start = Time.now.to_i - 1
26
+ @step = 300
27
+ @r.create @step, @start, [ "DS:a:GAUGE:#{2*@step}:0:1",
28
+ "DS:b:GAUGE:#{2*@step}:-10:10",
29
+ "RRA:AVERAGE:0.5:1:300",
30
+ "RRA:LAST:0.5:1:10" ]
31
+ end
32
+
33
+ def create_data
34
+ scale = 10
35
+ ts = Time.now.to_i + 1
36
+ while (ts < (@start.to_i + @step*300)) do
37
+ @r.update "a:b", ["#{ts}:#{rand()}:#{Math.sin(ts / @step / 10)}"]
38
+ ts += @step
39
+ end
40
+ end
41
+
42
+ def test_create
43
+ create_file
44
+ assert File.readable?(@f)
45
+ end
46
+
47
+ def test_dump
48
+ # RRDtool.dump is not defined for older versions of RRDtool
49
+ dump_file = "#{@f}.out"
50
+ create_file
51
+ create_data
52
+ if RRDtool.version < 1.2015 then
53
+ assert_raise NoMethodError do
54
+ @r.dump dump_file
55
+ end
56
+ else
57
+ @r.dump dump_file
58
+ assert File.readable? dump_file
59
+ end
60
+ # cleanup
61
+ File.delete dump_file if File.exists? dump_file
62
+ end
63
+
64
+ def test_fetch
65
+ assert_raise RRDtoolError do
66
+ @r.fetch ["AVERAGE"]
67
+ end
68
+ create_file
69
+ create_data
70
+ fary = @r.fetch ["AVERAGE"]
71
+ assert_not_nil fary
72
+ assert !fary.empty?
73
+ #assert_equals expected_data_points, fary[3]
74
+ # LATER: test rrd.fetch ["AVERAGE", "LAST"]
75
+ end
76
+
77
+ def test_first
78
+ # first and last should raise an exception without an RRD file
79
+ assert_raise RRDtoolError do
80
+ @r.first 0
81
+ end
82
+ create_file
83
+ (0..1).each do |i|
84
+ f = @r.first i
85
+ assert_not_nil f
86
+ assert f > 0
87
+ end
88
+ assert_raise RRDtoolError do
89
+ f2 = @r.first 2
90
+ end
91
+ end
92
+
93
+ def test_graph
94
+ create_file
95
+ create_data
96
+ RRDtool.graph(
97
+ [@g, "--title", " RubyRRD Demo",
98
+ "--start", "#{@start} + 1 h",
99
+ "--end", "#{@start} + 1000 min",
100
+ "--interlace",
101
+ "--imgformat", "PNG",
102
+ "--width=450",
103
+ "DEF:a=#{@f}:a:AVERAGE",
104
+ "DEF:b=#{@f}: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
+ assert File.exists?(@g)
110
+ end
111
+
112
+ def test_info
113
+ create_file
114
+ info = @r.info
115
+ assert_not_nil info
116
+ assert_equal info['filename'], @f
117
+ assert_equal info['step'], @step
118
+ end
119
+
120
+ def test_last
121
+ # first and last should raise an exception without an RRD file
122
+ assert_raise RRDtoolError do
123
+ @r.last
124
+ end
125
+ create_file
126
+ l = @r.last
127
+ assert l > 0
128
+ assert_equal @start, l
129
+ create_data
130
+ l = @r.last
131
+ assert l > @start
132
+ end
133
+
134
+ def test_resize
135
+ raise NotImplementedError, 'Need to write test_resize'
136
+ end
137
+
138
+ def test_restore
139
+ raise NotImplementedError, 'Need to write test_restore'
140
+ end
141
+
142
+ def test_rrdname
143
+ r = RRDtool.new 'foo'
144
+ assert_equal r.rrdname, 'foo'
145
+ end
146
+
147
+ def test_tune
148
+ raise NotImplementedError, 'Need to write test_tune'
149
+ end
150
+
151
+ def test_update
152
+ create_file
153
+ # first, test the simple update case (now)
154
+ @r.update "a:b", ["N:0:0"]
155
+ assert_in_delta Time.now.to_i, @r.last, 1
156
+ # next, create a bunch of data entries
157
+ create_data
158
+ l = @r.last
159
+ @r.update "a:b", ["#{l+1}:0:0", "#{l+2}:1:1"]
160
+ assert_equal @r.last, l+2
161
+ end
162
+
163
+ def test_version
164
+ v = RRDtool.version
165
+ assert_instance_of Float, v
166
+ assert_not_nil v
167
+ end
168
+
169
+ def test_xport
170
+ raise NotImplementedError, 'Need to write test_xport'
171
+ end
172
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: errand
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.0
5
+ platform: ruby
6
+ authors:
7
+ - Lindsay Holmwood
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-22 00:00:00 +13:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Errand provides Ruby bindings for RRD functions (via librrd), and a concise DSL for interacting with RRDs.
17
+ email: lindsay@holmwood.id.au
18
+ executables: []
19
+
20
+ extensions:
21
+ - extconf.rb
22
+ extra_rdoc_files:
23
+ - README.md
24
+ files:
25
+ - .cvsignore
26
+ - .gitignore
27
+ - AUTHORS
28
+ - README.md
29
+ - Rakefile
30
+ - VERSION
31
+ - extconf.rb
32
+ - lib/errand.rb
33
+ - lib/graph.rb
34
+ - rrd_info.h
35
+ - rrdtool_main.c
36
+ - spec/errand_spec.rb
37
+ - spec/spec.opts
38
+ - test/test_rrdtool.rb
39
+ has_rdoc: true
40
+ homepage: http://auxesis.github.com/errand
41
+ licenses: []
42
+
43
+ post_install_message:
44
+ rdoc_options:
45
+ - --charset=UTF-8
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ requirements: []
61
+
62
+ rubyforge_project:
63
+ rubygems_version: 1.3.5
64
+ signing_key:
65
+ specification_version: 3
66
+ summary: Ruby language binding for RRD tool version 1.2+
67
+ test_files:
68
+ - spec/errand_spec.rb
69
+ - test/test_rrdtool.rb