gri 10.0.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.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/MIT-LICENSE.txt +20 -0
  4. data/README.md +132 -0
  5. data/Rakefile +5 -0
  6. data/bin/grapher +17 -0
  7. data/bin/gri +5 -0
  8. data/bin/gricast +17 -0
  9. data/bin/grispark +5 -0
  10. data/bin/griwalk +9 -0
  11. data/bin/trad +19 -0
  12. data/bin/trad-http +17 -0
  13. data/gri.gemspec +21 -0
  14. data/lib/fluent/plugin/out_gri.rb +56 -0
  15. data/lib/gri/api.rb +28 -0
  16. data/lib/gri/app_collector.rb +154 -0
  17. data/lib/gri/app_walker.rb +23 -0
  18. data/lib/gri/blank.rb +49 -0
  19. data/lib/gri/builtindefs.rb +211 -0
  20. data/lib/gri/cast.rb +84 -0
  21. data/lib/gri/cgraph.rb +37 -0
  22. data/lib/gri/clist.rb +163 -0
  23. data/lib/gri/collector.rb +284 -0
  24. data/lib/gri/config.rb +134 -0
  25. data/lib/gri/ds_list.rb +166 -0
  26. data/lib/gri/format_helper.rb +112 -0
  27. data/lib/gri/gparams.rb +43 -0
  28. data/lib/gri/graph.rb +239 -0
  29. data/lib/gri/grapher.rb +70 -0
  30. data/lib/gri/ldb.rb +160 -0
  31. data/lib/gri/list.rb +242 -0
  32. data/lib/gri/log.rb +140 -0
  33. data/lib/gri/loop.rb +109 -0
  34. data/lib/gri/ltsv.rb +58 -0
  35. data/lib/gri/main.rb +107 -0
  36. data/lib/gri/mlog.rb +22 -0
  37. data/lib/gri/mmsgpack.rb +57 -0
  38. data/lib/gri/msnmp.rb +601 -0
  39. data/lib/gri/page.rb +235 -0
  40. data/lib/gri/pcollector.rb +209 -0
  41. data/lib/gri/plugin.rb +75 -0
  42. data/lib/gri/plugin/bootstrap.rb +65 -0
  43. data/lib/gri/plugin/cisco.rb +98 -0
  44. data/lib/gri/plugin/exec_collector.rb +89 -0
  45. data/lib/gri/plugin/juniper.rb +5 -0
  46. data/lib/gri/plugin/netsnmp.rb +8 -0
  47. data/lib/gri/plugin/ucdavis.rb +176 -0
  48. data/lib/gri/plugin/writer_fluentd.rb +26 -0
  49. data/lib/gri/polling_unit.rb +88 -0
  50. data/lib/gri/q.rb +5 -0
  51. data/lib/gri/request.rb +29 -0
  52. data/lib/gri/rrd.rb +438 -0
  53. data/lib/gri/scheduler.rb +68 -0
  54. data/lib/gri/sgraph.rb +147 -0
  55. data/lib/gri/spark.rb +94 -0
  56. data/lib/gri/tra_collector.rb +80 -0
  57. data/lib/gri/trad.rb +170 -0
  58. data/lib/gri/updater.rb +201 -0
  59. data/lib/gri/util_daemon.rb +19 -0
  60. data/lib/gri/util_marshal.rb +13 -0
  61. data/lib/gri/utils.rb +63 -0
  62. data/lib/gri/vendor.rb +76 -0
  63. data/lib/gri/version.rb +3 -0
  64. data/lib/gri/wmain.rb +67 -0
  65. data/lib/gri/writer.rb +184 -0
  66. data/mcollector +47 -0
  67. data/test/mock.rb +60 -0
  68. data/test/root/gra/.sysdb/sysdb.txt +3 -0
  69. data/test/root/gra/testhost/.records.txt +2 -0
  70. data/test/root/if.def +2 -0
  71. data/test/root/test.conf +4 -0
  72. data/test/root/testtab +9 -0
  73. data/test/root/testtab2 +2 -0
  74. data/test/root/tra/testhost/_/20130614 +20 -0
  75. data/test/test_app.rb +58 -0
  76. data/test/test_builtindefs.rb +24 -0
  77. data/test/test_collector.rb +58 -0
  78. data/test/test_config.rb +62 -0
  79. data/test/test_ds_list.rb +48 -0
  80. data/test/test_exec_collector.rb +33 -0
  81. data/test/test_format_helper.rb +68 -0
  82. data/test/test_graph.rb +69 -0
  83. data/test/test_ldb.rb +29 -0
  84. data/test/test_list.rb +65 -0
  85. data/test/test_log.rb +16 -0
  86. data/test/test_loop.rb +35 -0
  87. data/test/test_ltsv.rb +38 -0
  88. data/test/test_main.rb +19 -0
  89. data/test/test_mmsgpack.rb +27 -0
  90. data/test/test_msnmp.rb +147 -0
  91. data/test/test_page.rb +51 -0
  92. data/test/test_pcollector.rb +71 -0
  93. data/test/test_plugin.rb +62 -0
  94. data/test/test_plugin_cisco.rb +23 -0
  95. data/test/test_polling_unit.rb +58 -0
  96. data/test/test_request.rb +26 -0
  97. data/test/test_rrd.rb +53 -0
  98. data/test/test_rrd_updater.rb +139 -0
  99. data/test/test_scheduler.rb +31 -0
  100. data/test/test_tra_collector.rb +40 -0
  101. data/test/test_trad.rb +33 -0
  102. data/test/test_util_marshal.rb +17 -0
  103. data/test/test_utils.rb +15 -0
  104. data/test/test_vendor.rb +40 -0
  105. data/test/test_writer.rb +33 -0
  106. data/test/unittest_helper.rb +27 -0
  107. metadata +208 -0
@@ -0,0 +1,26 @@
1
+ require 'gri/writer'
2
+ unless Object.const_defined? :MessagePack
3
+ require 'gri/mmsgpack'
4
+ end
5
+
6
+ module GRI
7
+ class FluentWriter < Writer
8
+ TYPES['fluent'] = self
9
+ TYPES['fluentd'] = self
10
+
11
+ def initialize options={}
12
+ @options = options
13
+ host = options[:fluent_host]
14
+ @sock = TCPSocket.new host, 24224
15
+ end
16
+
17
+ def write records
18
+ time = Time.now.to_i
19
+ for record in records
20
+ tag = @options[:fluent_tag] || "gri.#{record['_key']}"
21
+ s = [tag, time, record].to_msgpack
22
+ @sock.write s
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,88 @@
1
+ module GRI
2
+ class PollingUnit
3
+ UNITS = {}
4
+
5
+ attr_reader :name, :cat, :oids
6
+ attr_accessor :dhash
7
+ alias :defs :dhash
8
+
9
+ def self.all_units
10
+ if UNITS.empty?
11
+ for name, dhash in DEFS
12
+ next unless String === name
13
+ pucat = dhash[:cat] || dhash[:pucat] ||
14
+ (dhash[:tdb] and dhash[:tdb].first.intern) || name.intern
15
+ puclass = dhash[:puclass]
16
+ if puclass and Object.const_defined?(puclass + 'PollingUnit')
17
+ klass = eval(puclass + 'PollingUnit')
18
+ else
19
+ klass = self
20
+ end
21
+ pu = klass.new name, pucat
22
+ pu.dhash = dhash
23
+ pu.set_oids dhash[:oid]
24
+
25
+ self::UNITS[name] = pu
26
+ end
27
+ end
28
+ self::UNITS
29
+ end
30
+
31
+ def initialize name, cat
32
+ @name = name
33
+ @cat = cat
34
+ @d_p = false
35
+ end
36
+
37
+ def set_oids names
38
+ @oids = (names || []).map {|name|
39
+ (oid = SNMP::OIDS[name]) ? BER.enc_v_oid(oid) :
40
+ (Log.debug "No such OID: #{name}"; nil)
41
+ }.compact
42
+ end
43
+
44
+ def feed wh, enoid, tag, val
45
+ if (feed_proc = dhash[:feed])
46
+ puts " feed_proc #{[enid, tag, val].inspect}" if @d_p
47
+ feed_proc.call wh, enoid, tag, val
48
+ else
49
+ if enoid.getbyte(-2) < 128
50
+ ind = enoid.getbyte(-1)
51
+ if ind == 0
52
+ oid_ind = enoid
53
+ else
54
+ oid_ind = enoid[0..-2]
55
+ end
56
+ else
57
+ if enoid.getbyte(-3) < 128
58
+ ind = ((enoid.getbyte(-2) & 0x7f) << 7) + enoid.getbyte(-1)
59
+ oid_ind = enoid[0..-3]
60
+ else
61
+ tmpary = BER.dec_oid enoid
62
+ oid_ind = BER.enc_v_oid(tmpary[0..-2].join('.'))
63
+ ind = tmpary[-1]
64
+ end
65
+ end
66
+ if (sym_oid = SNMP::ROIDS[oid_ind])
67
+ (conv_val_proc = dhash[:conv_val]) and
68
+ (val = conv_val_proc.call(sym_oid, val))
69
+ (wh[ind] ||= {})[sym_oid] = val
70
+ if @d_p
71
+ wh[ind]['_d'] = true
72
+ puts " feed #{[sym_oid, ind, tag, val].inspect}"
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ def fix_workhash workhash
79
+ if (c = dhash[:fix_workhash])
80
+ c.call workhash
81
+ end
82
+ end
83
+
84
+ def inspect
85
+ "#<PU:#{@name}>"
86
+ end
87
+ end
88
+ end
data/lib/gri/q.rb ADDED
@@ -0,0 +1,5 @@
1
+ def q(*args)
2
+ if $debug or $test
3
+ STDERR.puts args.map {|item| item.inspect}.join(', ')
4
+ end
5
+ end
@@ -0,0 +1,29 @@
1
+ require 'rack/request'
2
+ require 'gri/gparams'
3
+
4
+ module GRI
5
+ class Request < Rack::Request
6
+ def query_string=(s)
7
+ @query_string = s
8
+ @gparams = @params = nil
9
+ end
10
+
11
+ alias query_string0 query_string
12
+ def query_string
13
+ @query_string || query_string0
14
+ end
15
+
16
+ def gparams
17
+ @gparams ||= gparse_query query_string
18
+ end
19
+
20
+ def gparse_query qs
21
+ params = GParams.new
22
+ (qs || '').split(/[&;] */n).each {|item|
23
+ k, v = item.split('=', 2).map {|s| Rack::Utils.unescape s}
24
+ params[k] = v
25
+ }
26
+ params
27
+ end
28
+ end
29
+ end
data/lib/gri/rrd.rb ADDED
@@ -0,0 +1,438 @@
1
+ require 'open3'
2
+
3
+ class RRDError < Exception; end
4
+
5
+ module RRDm
6
+ extend RRDm
7
+
8
+ @@rrdtool_path = (Dir.glob('/usr/local/rrdtool-*/bin/rrdtool').sort.reverse +
9
+ ['/usr/local/bin/rrdtool', '/usr/bin/rrdtool']).find {|path|
10
+ File.executable? path.untaint
11
+ }
12
+
13
+ attr_reader :version
14
+
15
+ def start rrdtool_path=nil
16
+ rrdtool_path ||= @@rrdtool_path
17
+ raise RuntimeError, 'rrdtool is already running' if @sequence
18
+ unless rrdtool_path and File.exist?(rrdtool_path)
19
+ raise Exception, "rrdtool #{rrdtool_path} not found"
20
+ end
21
+ @sequence = 'S'
22
+ @last_cmd = nil
23
+
24
+ @in, @out, @err = Open3.popen3("#{rrdtool_path} -")
25
+ @in.sync = true
26
+ @out.sync = true
27
+
28
+ cmd 'version'
29
+ line, = read
30
+ @version, = line.scan(/^RRDtool\s+(\S+)/)[0]
31
+ end
32
+
33
+ def running?
34
+ @sequence
35
+ end
36
+
37
+ def read out=nil
38
+ unless @sequence == 'C'
39
+ raise RuntimeError, 'RRDm.read can only be called after RRDm.cmd'
40
+ end
41
+ @sequence == 'R'
42
+ lines = []
43
+ errline = nil
44
+
45
+ while line = @out.gets
46
+ if line =~ /^ERROR/
47
+ errline = line.gsub(/\n/, "\\n")
48
+ raise RuntimeError, errline
49
+ elsif line =~ /^OK u:([\d\.]+) s:([\d\.]+) r:([\d\.]+)/
50
+ sys, user, real = $1.to_f, $2.to_f, $3.to_f
51
+ if errline
52
+ raise RuntimeError, errline
53
+ else
54
+ return [lines.join(''), sys, user, real]
55
+ end
56
+ end
57
+ if out
58
+ out.write line
59
+ else
60
+ lines.push line
61
+ end
62
+ end
63
+ raise RuntimeError, 'unexpected EOF'
64
+ end
65
+
66
+ def cmd(*args)
67
+ @sequence = 'C'
68
+ cmd = args.join(' ')
69
+ @in.print cmd, "\n"
70
+ @last_cmd = cmd
71
+ end
72
+
73
+ def close
74
+ @sequence = nil
75
+ @in.close
76
+ @out.close
77
+ @err.close
78
+ end
79
+ end
80
+
81
+ class RRD
82
+ attr_accessor :width, :height
83
+ attr_accessor :title, :vertical_label
84
+ attr_accessor :imgformat
85
+ attr_accessor :lower_limit
86
+ attr_accessor :x_grid_hash
87
+ attr_accessor :fonts
88
+ attr_reader :created_p
89
+ attr_reader :ds_ary
90
+ attr_reader :version
91
+ attr_writer :show_graph_date
92
+
93
+ def initialize address=nil, base_dir=nil
94
+ @address = address
95
+ @base_dir = base_dir
96
+
97
+ @imgformat = 'PNG'
98
+ @update_buf = []
99
+ @x_grid_hash = {}
100
+ @show_graph_date = false
101
+ @fonts = []
102
+ RRDm.start unless RRDm.running?
103
+ @version = (RRDm.version =~ /\A(1\.\d)\./) ? $1 : '1.0'
104
+ end
105
+
106
+ def set_create_args *args
107
+ @rrdname = args.shift
108
+ if @rrdname
109
+ @created_p = File.exist? @rrdname
110
+ @args = args
111
+ end
112
+ end
113
+
114
+ def remove_base_dir *args
115
+ return args unless @base_dir
116
+ if @base_dir[-1, 1] == ?/
117
+ base_dir = @base_dir
118
+ else
119
+ base_dir = @base_dir + '/'
120
+ end
121
+ re = /\A#{base_dir}/
122
+ args.map {|arg| arg.sub(re, '')}
123
+ end
124
+
125
+ def create start=nil, step=nil, *args
126
+ str = ''
127
+ if start
128
+ start = start.to_i
129
+ str = "--start #{start} "
130
+ end
131
+ if step
132
+ str += "--step #{step}"
133
+ end
134
+ RRDm.cmd 'create', @rrdname, str, *args
135
+ return RRDm.read
136
+ end
137
+ private :create
138
+
139
+ def buffered_update(*args)
140
+ res = flush_buffer if @update_buf.size > 40
141
+ @update_buf += args
142
+ res
143
+ end
144
+
145
+ def flush_buffer v=false
146
+ if @update_buf.size > 0
147
+ begin
148
+ unless @created_p
149
+ create *@args
150
+ @created_p = true
151
+ end
152
+ res = update *@update_buf
153
+ end
154
+ @update_buf = []
155
+ end
156
+ res
157
+ end
158
+
159
+ def update(*args)
160
+ daemon = @address ? " --daemon #{@address}" : ''
161
+ path = remove_base_dir @rrdname
162
+ RRDm.cmd "update#{daemon}", path.first, *args
163
+ begin
164
+ str, = RRDm.read
165
+ rescue RuntimeError => e
166
+ if e.message =~ /last update time is (\d+)/
167
+ last_update = $1.to_i
168
+ pargs = []
169
+ args.each {|arg|
170
+ if arg.split(':')[0].to_i > last_update
171
+ pargs.push arg
172
+ end
173
+ }
174
+ if pargs.size == 0
175
+ return nil
176
+ end
177
+ args = pargs
178
+ RRDm.cmd "update#{daemon}", path, *args
179
+ str, = RRDm.read rescue nil
180
+ else
181
+ Log.error "#{@rrdname}: #{$!}"
182
+ end
183
+ end
184
+ lines = str.split(/\n/).select {|line| line =~ /^\[/} if str
185
+ lines
186
+ end
187
+
188
+ def graph fname, starttime, endtime, *misc_args
189
+ starttime = starttime.to_i
190
+ endtime = endtime.to_i
191
+ if misc_args.first.kind_of? Symbol
192
+ cmd = misc_args.shift.to_s
193
+ else
194
+ daemon = @address ? " --daemon #{@address}" : ''
195
+ cmd = "graph#{daemon}"
196
+ end
197
+ args = [cmd, fname, '--start', starttime, '--end', endtime]
198
+ args.push '--slope-mode' if @version >= '1.2'
199
+ args.push '--width ' + @width.to_s if @width
200
+ args.push '--height ' + @height.to_s if @height
201
+ args.push '--lower-limit ' + @lower_limit.to_s if @lower_limit
202
+ args.push '--imgformat ' + @imgformat
203
+ args.push '--title ' + @title if @title
204
+ args.push '--vertical-label ' + @vertical_label if @vertical_label
205
+ if @version >= '1.3'
206
+ for fstr in @fonts
207
+ (fstr =~ /^(DEFAULT|TITLE|AXIS|UNIT|LEGEND):(\d+):/) and
208
+ args.push "-n \"#{fstr}\""
209
+ end
210
+ end
211
+
212
+ dt = endtime - starttime
213
+ vtime = Time.at(starttime)
214
+ vcolor = '#ff7f7f'
215
+ xgrid = nil
216
+ if dt < 3 * 3600
217
+ vtime = Time.local(vtime.year, vtime.mon, vtime.day) + 24*3600
218
+ args.push "VRULE:#{vtime.to_i}#{vcolor}" if vtime.to_i < endtime
219
+ xgrid = 'MINUTE:10:HOUR:1:MINUTE:10:0:%H:%M'
220
+ elsif dt < 12 * 3600
221
+ vtime = Time.local(vtime.year, vtime.mon, vtime.day) + 24*3600
222
+ args.push "VRULE:#{vtime.to_i}#{vcolor}" if vtime.to_i < endtime
223
+ xgrid = 'MINUTE:30:HOUR:1:HOUR:1:0:%H:%M'
224
+ elsif dt < 6 * 24 * 3600
225
+ vtime = Time.local(vtime.year, vtime.mon, vtime.day) + 24*3600
226
+ while vtime.to_i < endtime
227
+ args.push "VRULE:#{vtime.to_i}#{vcolor}"
228
+ vtime += 24*3600
229
+ end
230
+ xgrid = 'HOUR:1:HOUR:6:HOUR:6:0:%H:%M'
231
+ elsif dt < 14 * 24 * 3600
232
+ tmptime = vtime + ((8 - vtime.wday) % 7)*24*3600
233
+ vtime = Time.local(tmptime.year, tmptime.mon, tmptime.day)
234
+ while vtime.to_i < endtime
235
+ args.push "VRULE:#{vtime.to_i}#{vcolor}"
236
+ vtime += 7*24*3600
237
+ end
238
+ xgrid = 'HOUR:6:DAY:1:DAY:1:86400:%a'
239
+ elsif dt < 90 * 24 * 3600
240
+ tmptime = Time.local(vtime.year, vtime.mon) + 31*24*3600 + 3610
241
+ vtime = Time.local(tmptime.year, tmptime.mon)
242
+ while vtime.to_i < endtime
243
+ args.push "VRULE:#{vtime.to_i}#{vcolor}"
244
+ tmptime = vtime + 31*24*3600 + 3610
245
+ vtime = Time.local(tmptime.year, tmptime.mon, 1)
246
+ end
247
+ xgrid = 'DAY:1:WEEK:1:WEEK:1:86400:%m/%d'
248
+ else
249
+ vtime = Time.local(vtime.year+1)
250
+ while vtime.to_i < endtime
251
+ args.push "VRULE:#{vtime.to_i}#{vcolor}"
252
+ vtime = Time.local(vtime.year+1)
253
+ end
254
+ xgrid = 'MONTH:3:YEAR:1:YEAR:1:31536000:%Y' if dt > 400 * 24 * 3600
255
+ end
256
+ args.push "--x-grid \"#{xgrid}\"" if xgrid
257
+
258
+ if @show_graph_date
259
+ cstr = "COMMENT:" +
260
+ Time.at(starttime).strftime('"%Y/%m/%d %H:%M - ') +
261
+ Time.at(endtime).strftime('%Y/%m/%d %H:%M\n"')
262
+ args.push cstr
263
+ end
264
+
265
+ args += misc_args
266
+ RRDm.cmd args
267
+ return RRDm.read
268
+ end
269
+
270
+ def fetch cf, resolution=nil, starttime=nil, endtime=nil
271
+ args = []
272
+ if resolution
273
+ resolution = resolution.to_i
274
+ if starttime and endtime
275
+ starttime = starttime.to_i / resolution * resolution
276
+ endtime = endtime.to_i / resolution * resolution
277
+ end
278
+ args.push ['--resolution', resolution]
279
+ end
280
+ args.push ['--start', starttime.to_i] if starttime
281
+ args.push ['--end', endtime.to_i] if endtime
282
+ RRDm.cmd 'fetch', @rrdname, cf, *args
283
+ str, = RRDm.read
284
+ collect = []
285
+ s = nil #XXX
286
+ for line in str.split("\n")
287
+ timestr, *remain = line.split
288
+ time = timestr.to_i
289
+ if time > 0
290
+ collect.push [Time.at(time),
291
+ remain.collect {|s|
292
+ if s =~ /^nan$/i
293
+ nil
294
+ else
295
+ s.to_f
296
+ end
297
+ }]
298
+ else
299
+ ds_ary = line.split
300
+ ds_ary.shift
301
+ @ds_ary = ds_ary if ds_ary.size > 0
302
+ end
303
+ end
304
+ return collect
305
+ end
306
+
307
+ def tune args
308
+ RRDm.cmd 'tune', @rrdname, *args
309
+ str, = RRDm.read
310
+ return str.split("\n")
311
+ end
312
+
313
+ def resize num, deltasize
314
+ if deltasize >= 0
315
+ rrcmd = 'GROW'
316
+ else
317
+ rrcmd = 'SHRINK'
318
+ end
319
+ RRDm.cmd 'resize', @rrdname, num, rrcmd, deltasize.abs
320
+ str, = RRDm.read
321
+ File.rename 'resize.rrd', @rrdname
322
+ return str.split("\n")
323
+ end
324
+
325
+ def info
326
+ RRDm.cmd 'info', @rrdname
327
+ str, = RRDm.read
328
+ return str.split("\n")
329
+ end
330
+
331
+ def dump dst=nil
332
+ tmp_path = dst || "/tmp/rrdtmp#{$$}.xml"
333
+ open(tmp_path, 'w') {|out|
334
+ RRDm.cmd 'dump', @rrdname#, tmp_path
335
+ str, = RRDm.read out
336
+ }
337
+ return tmp_path
338
+ end
339
+
340
+ def restore src=nil
341
+ tmp_path = src
342
+ RRDm.cmd 'restore', tmp_path, @rrdname
343
+ str, = RRDm.read
344
+ return tmp_path
345
+ end
346
+
347
+ def xport starttime, endtime, *misc_args
348
+ starttime = starttime.to_i
349
+ endtime = endtime.to_i
350
+ cmd = "xport#{@address ? " --daemon #{@address}" : ''}"
351
+ args = [cmd, '--json', '--start', starttime, '--end', endtime] + misc_args
352
+ RRDm.cmd args
353
+ str, = RRDm.read
354
+ return str
355
+ end
356
+
357
+ def rrainfo
358
+ ary = info
359
+ rra = []
360
+ step, = ary[2].scan(/^step = (\d+)$/)[0]
361
+ if step
362
+ for line in ary
363
+ if line =~ /^rra\[(\d+)\]\.(.*)/
364
+ num = $1.to_i
365
+ remain = $2
366
+ #p [$1, remain]
367
+ rra[num] = [] unless rra[num]
368
+ case remain
369
+ when /^cf = "(\w+)"/
370
+ rra[num][0] = $1
371
+ when /^rows = (\d+)/
372
+ rra[num][1] = $1.to_i
373
+ when /^pdp_per_row = (\d+)/
374
+ rra[num][2] = $1.to_i
375
+ end
376
+ end
377
+ end
378
+ end
379
+ return step, rra
380
+ end
381
+
382
+ def graphgen stime, etime, args
383
+ tmp_file = "/tmp/rrdtmp#{$$}"
384
+ img = nil
385
+ graph tmp_file, stime, etime, args
386
+ open(tmp_file) {|f| img = f.read}
387
+ File.unlink tmp_file
388
+ img
389
+ end
390
+
391
+ def self.defstr rrdpaths, ds, cf, cname, line, color, legend,
392
+ mag=1, lower_bound=nil, upper_bound=nil
393
+ expr = []
394
+ st = {}
395
+ n = 0
396
+
397
+ limitstr = if lower_bound and upper_bound and (lower_bound != upper_bound)
398
+ ",#{lower_bound},#{upper_bound},LIMIT"
399
+ elsif lower_bound
400
+ ",DUP,#{lower_bound},GE,EXC,UNKN,IF"
401
+ else
402
+ ''
403
+ end
404
+ if rrdpaths.size > 1
405
+ vnames = []
406
+ for rrdpath in rrdpaths
407
+ basename = File.basename rrdpath, '.rrd'
408
+ vname = "v#{st[basename] ||= (n+=1)}#{ds}"
409
+ vnames.push vname
410
+ expr.push "DEF:#{vname}=#{rrdpath}:#{ds}:#{cf} "
411
+ end
412
+ tmpstr = "CDEF:#{cname}=" +
413
+ vnames.map {|vname|
414
+ "#{vname},UN,0,#{vname},IF,#{mag},*#{limitstr}"
415
+ }.join(',') + ',+' * (vnames.size-1)
416
+ expr.push tmpstr
417
+ #expr.push ',FLOOR' if ds =~ /(in|out)ucast/
418
+ else
419
+ rrdpath, = rrdpaths
420
+ expr.push "DEF:v#{cname}=#{rrdpath}:#{ds}:#{cf} "
421
+ expr.push "CDEF:#{cname}=v#{cname}#{limitstr},#{mag},* "
422
+ end
423
+
424
+ if line == 'XPORT'
425
+ expr.push " XPORT:#{cname}:\"#{legend}\""
426
+ else
427
+ expr.push " #{line}:#{cname}#{color}"
428
+ unless legend == '-'
429
+ esc_legend = legend.gsub(':', '\:')
430
+ expr.push ":\"#{esc_legend}\" "
431
+ expr.push "GPRINT:#{cname}:MAX:\"(max\\:%.2lf%s\" "
432
+ expr.push "GPRINT:#{cname}:AVERAGE:\"avg\\:%.2lf%s\" "
433
+ expr.push "GPRINT:#{cname}:LAST:\"last\\:%.2lf%s)\""
434
+ end
435
+ end
436
+ return expr.join('')
437
+ end
438
+ end