collectd-interface 0.3.1 → 0.5.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 (126) hide show
  1. data/bin/collectd-interface-daemon +21 -361
  2. data/bin/collectd-interface-plugins +11 -11
  3. data/lib/collectd/interface/config.rb +117 -0
  4. data/lib/collectd/interface/options.rb +68 -0
  5. data/lib/collectd/interface/service/data.rb +110 -0
  6. data/lib/collectd/interface/service/graph.rb +106 -0
  7. data/lib/collectd/interface/service/report.rb +82 -0
  8. data/lib/collectd/interface/service.rb +44 -0
  9. data/public/images/cpus.png +0 -0
  10. data/public/images/device/ops/sda.png +0 -0
  11. data/public/images/device/ops/sda1.png +0 -0
  12. data/public/images/device/ops/sda2.png +0 -0
  13. data/public/images/device/ops/sda5.png +0 -0
  14. data/public/images/device/ops/sda6.png +0 -0
  15. data/public/images/device/ops/sda7.png +0 -0
  16. data/public/images/device/ops/sda8.png +0 -0
  17. data/public/images/device/ops/sda9.png +0 -0
  18. data/public/images/device/ops/sdb.png +0 -0
  19. data/public/images/device/ops/sdb1.png +0 -0
  20. data/public/images/device/ops/sdh.png +0 -0
  21. data/public/images/device/ops/sdh1.png +0 -0
  22. data/public/images/device/ops/sdh2.png +0 -0
  23. data/public/images/device/ops/sdh3.png +0 -0
  24. data/public/images/device/ops/sdh4.png +0 -0
  25. data/public/images/device/ops/sr0.png +0 -0
  26. data/public/images/device/time/sda.png +0 -0
  27. data/public/images/device/time/sda1.png +0 -0
  28. data/public/images/device/time/sda2.png +0 -0
  29. data/public/images/device/time/sda5.png +0 -0
  30. data/public/images/device/time/sda6.png +0 -0
  31. data/public/images/device/time/sda7.png +0 -0
  32. data/public/images/device/time/sda8.png +0 -0
  33. data/public/images/device/time/sda9.png +0 -0
  34. data/public/images/device/time/sdb.png +0 -0
  35. data/public/images/device/time/sdb1.png +0 -0
  36. data/public/images/device/time/sdh.png +0 -0
  37. data/public/images/device/time/sdh1.png +0 -0
  38. data/public/images/device/time/sdh2.png +0 -0
  39. data/public/images/device/time/sdh3.png +0 -0
  40. data/public/images/device/time/sdh4.png +0 -0
  41. data/public/images/device/time/sr0.png +0 -0
  42. data/public/images/device/traffic/sda.png +0 -0
  43. data/public/images/device/traffic/sda1.png +0 -0
  44. data/public/images/device/traffic/sda2.png +0 -0
  45. data/public/images/device/traffic/sda5.png +0 -0
  46. data/public/images/device/traffic/sda6.png +0 -0
  47. data/public/images/device/traffic/sda7.png +0 -0
  48. data/public/images/device/traffic/sda8.png +0 -0
  49. data/public/images/device/traffic/sda9.png +0 -0
  50. data/public/images/device/traffic/sdb.png +0 -0
  51. data/public/images/device/traffic/sdb1.png +0 -0
  52. data/public/images/device/traffic/sdh.png +0 -0
  53. data/public/images/device/traffic/sdh1.png +0 -0
  54. data/public/images/device/traffic/sdh2.png +0 -0
  55. data/public/images/device/traffic/sdh3.png +0 -0
  56. data/public/images/device/traffic/sdh4.png +0 -0
  57. data/public/images/device/traffic/sr0.png +0 -0
  58. data/public/images/irqs.png +0 -0
  59. data/public/images/load.png +0 -0
  60. data/public/images/memory.png +0 -0
  61. data/public/images/network/packets/eth0.png +0 -0
  62. data/public/images/network/packets/lo.png +0 -0
  63. data/public/images/network/packets/nbr0-nic.png +0 -0
  64. data/public/images/network/packets/nbr0.png +0 -0
  65. data/public/images/network/packets/virbr0.png +0 -0
  66. data/public/images/network/packets/vnet0.png +0 -0
  67. data/public/images/network/packets/vnet1.png +0 -0
  68. data/public/images/network/packets/vnet2.png +0 -0
  69. data/public/images/network/packets/vnet3.png +0 -0
  70. data/public/images/network/packets/vnet4.png +0 -0
  71. data/public/images/network/packets/vnet5.png +0 -0
  72. data/public/images/network/traffic/eth0.png +0 -0
  73. data/public/images/network/traffic/lo.png +0 -0
  74. data/public/images/network/traffic/nbr0-nic.png +0 -0
  75. data/public/images/network/traffic/nbr0.png +0 -0
  76. data/public/images/network/traffic/virbr0.png +0 -0
  77. data/public/images/network/traffic/vnet0.png +0 -0
  78. data/public/images/network/traffic/vnet1.png +0 -0
  79. data/public/images/network/traffic/vnet2.png +0 -0
  80. data/public/images/network/traffic/vnet3.png +0 -0
  81. data/public/images/network/traffic/vnet4.png +0 -0
  82. data/public/images/network/traffic/vnet5.png +0 -0
  83. data/public/images/processes.png +0 -0
  84. data/public/images/users.png +0 -0
  85. data/public/script/table_filter.js +1217 -0
  86. data/public/style/default.css +28 -0
  87. data/test/collectd/interface/config_test.rb +9 -0
  88. data/test/collectd/interface/service_config_test.rb +26 -0
  89. data/test/collectd/interface/service_data_test.rb +46 -0
  90. data/test/start.rb +3 -0
  91. data/views/graph_header.erb +13 -4
  92. data/views/graphs/device/ops.erb +37 -0
  93. data/views/graphs/device/time.erb +37 -0
  94. data/views/graphs/device/traffic.erb +38 -0
  95. data/views/graphs/{irq.erb.disabled → irqs.erb} +8 -8
  96. data/views/graphs/network/packets.erb +35 -0
  97. data/views/graphs/network/traffic.erb +35 -0
  98. data/views/graphs/{users.erb.disabled → users.erb} +0 -0
  99. data/views/report.erb +32 -14
  100. data/views/reports/open-files/local.erb +62 -0
  101. data/views/reports/{processes-cpu-usage.erb → processes.erb} +0 -0
  102. data/views/reports/{system-sockets.erb → sockets.erb} +0 -0
  103. data/views/reports/{disk-free.erb → storage.erb} +1 -1
  104. data/views/template/options/graph.erb +1 -1
  105. metadata +98 -30
  106. data/public/images/cpus.svg +0 -946
  107. data/public/images/disk-traffic-root.png +0 -0
  108. data/public/images/disk-traffic-srv.png +0 -0
  109. data/public/images/disk-traffic-tmp.png +0 -0
  110. data/public/images/disk-traffic-var.png +0 -0
  111. data/public/images/irq.png +0 -0
  112. data/public/images/load.svg +0 -638
  113. data/public/images/memory.svg +0 -741
  114. data/public/images/network-eth0.png +0 -0
  115. data/public/images/network-eth0.svg +0 -609
  116. data/public/images/network-lo.png +0 -0
  117. data/public/images/network-lo.svg +0 -644
  118. data/public/images/processes.svg +0 -832
  119. data/views/graphs/disk-traffic-root.erb +0 -24
  120. data/views/graphs/disk-traffic-srv.erb.disabled +0 -25
  121. data/views/graphs/disk-traffic-tmp.erb.disabled +0 -24
  122. data/views/graphs/disk-traffic-var.erb.disabled +0 -25
  123. data/views/graphs/network-eth0.erb +0 -23
  124. data/views/graphs/network-lo.erb +0 -23
  125. data/views/reports/list-open-files-lustre.erb.disabled +0 -44
  126. data/views/reports/list-open-files-tmp.erb +0 -44
@@ -1,40 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- #---------------------------------------------------------------
4
- #
5
- # Interface to Collectd
6
- #
7
- # This is free software: you can redistribute it
8
- # and/or modify it under the terms of the GNU General Public
9
- # License as published by the Free Software Foundation,
10
- # either version 3 of the License, or (at your option) any
11
- # later version.
12
- #
13
- # This program is distributed in the hope that it will be
14
- # useful, but WITHOUT ANY WARRANTY; without even the implied
15
- # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16
- # PURPOSE. See the GNU General Public License for more details.
17
- #
18
- # You should have received a copy of the GNU General Public
19
- # License along with this program. If not, see
20
- #
21
- # <http://www.gnu.org/licenses/>.
22
- #
23
- #----------------------------------------------------------------
24
- # Author: Victor Penso
25
- # Copyright 2012
26
- # Version: 0.1.0
27
- #----------------------------------------------------------------
28
-
29
-
30
- require 'rubygems'
31
- require 'getoptlong'
32
- require 'erb'
33
- require 'ostruct'
34
- require 'json'
3
+ require 'collectd/interface/options'
4
+ require 'collectd/interface/config'
5
+ require 'collectd/interface/service'
35
6
 
36
7
  exec_name = File.split(__FILE__)[-1]
37
-
38
8
  help = <<-EOF
39
9
  Synopsis
40
10
  ========
@@ -57,12 +27,12 @@ Options
57
27
  More verbose output while running this.
58
28
  --port,-p NUMBER:
59
29
  Start the REST server at port NUMBER.
60
- --log,-l PATH:
61
- Write the log output to PATH.
62
30
  --files, -f PATH:
63
31
  RRD files are located in PATH.
64
32
  Default is /var/lib/collectd/rrd
65
- --pid-file, -P PATH:
33
+ --log-file-path,-l PATH:
34
+ Write the log output to PATH.
35
+ --pid-file-path, -P PATH:
66
36
  Write the process ID to a file in PATH.
67
37
 
68
38
  Examples
@@ -70,345 +40,36 @@ Examples
70
40
 
71
41
  Run the stuff in debug-mode:
72
42
 
73
- #{exec_name} -d
43
+ #{exec_name} -d
74
44
 
75
45
  Start the server as root service:
76
46
 
77
-
78
- #{exec_name} -p 5000 -l /var/log/ -P /var/run/
47
+ #{exec_name} -p 5000 -l /var/log/ -P /var/run/
79
48
 
80
49
  EOF
81
50
 
82
- options = OpenStruct.new
83
- options.debug = false
84
- options.port = 4567
85
- options.log = nil
86
- options.files = '/var/lib/collectd/rrd'
87
- options.pid_file = nil
88
-
89
- GetoptLong.new(
90
- ['--help','-h',GetoptLong::NO_ARGUMENT],
91
- ['--debug','-d',GetoptLong::NO_ARGUMENT],
92
- ['--port','-p',GetoptLong::REQUIRED_ARGUMENT],
93
- ['--log','-l',GetoptLong::REQUIRED_ARGUMENT],
94
- ['--files','-f',GetoptLong::REQUIRED_ARGUMENT],
95
- ['--pid-file','-P',GetoptLong::REQUIRED_ARGUMENT]
96
- ).each do |opt,arg|
97
- case opt
98
- when '--port'
99
- options.port = arg
100
- when '--log'
101
- options.log = arg
102
- when '--files'
103
- options.files = arg
104
- when '--pid-file'
105
- options.pid_file = arg
106
- when '--debug'
107
- options.debug = true
108
- $DEBUG = true
109
- when '--help'
110
- $stdout.puts help
111
- exit 0
112
- end
113
- end
114
-
115
- # Didn't found a better way to pass configuration to the Sinatra instance
116
- ENV['COLLECTD_RRD_FILES'] = options.files
117
-
118
- require 'sinatra/base'
119
-
120
- class CollectdInterface < Sinatra::Base
121
-
122
- configure do
123
- hostname = `hostname -f`.chop
124
- # root directory of this software
125
- root = %Q[#{File.dirname(File.expand_path(__FILE__))}/..]
126
- # Sinatra configuration
127
- mime_type :json, 'application/json' # JSON as supported output format
128
- mime_type :plain, 'text/plain'
129
- disable :logging
130
-
131
- # Settings
132
- set :root, root
133
- set :rrd_path, "#{ENV['COLLECTD_RRD_FILES']}/#{hostname}/"
134
- set :public_folder, "#{root}/public"
135
- set :static, true
136
- set :environment, :production
137
-
138
- # Load all graphic plug-ins available
139
- graphs = Hash.new
140
- Dir["#{settings.root}/views/graphs/*.erb"].each do |file|
141
- graphs[File.basename(file,'.erb')] = file
142
- end
143
- set :plugins, graphs
144
-
145
- # List all RRD files available
146
- rrd_values = Array.new
147
- Dir["#{settings.rrd_path}/**/*.rrd"].each do |file|
148
- plugin = file.gsub(%r<#{settings.rrd_path}>,'').split('/')[1]
149
- rrd = File.basename(file,'.rrd')
150
- `rrdtool info "#{file}"`.scan(%r{ds\[(\w*)\]}).uniq.flatten.each do |set|
151
- rrd_values << "#{plugin}/#{rrd}/#{set}"
152
- end
153
- end
154
- set :rrd_values, rrd_values
155
-
156
- # List all report templates
157
- reports = Hash.new
158
- Dir["#{settings.root}/views/reports/*.erb"].each { |f| reports[File.basename(f,'.erb')] = f }
159
- set :reports, reports
160
- end
161
-
162
- get '/data' do
163
- @data = settings.rrd_values
164
- if params.has_key?('display')
165
- @target = params['display']
166
- params.delete('display')
167
- p = Array.new; params.each_pair { |k,v| p << "#{k}=#{v}" }
168
- @args = p.join('&')
169
- redirect "/data/#{@target}?#{@args}"
170
- elsif params.has_key?('format')
171
- case params['format']
172
- when 'json'
173
- content_type :json
174
- JSON.pretty_generate @data.map! { |d| "/data/#{d}" }
175
- else
176
- content_type :plain
177
- @data.join("\n")
178
- end
179
- else
180
- @target = 'data'
181
- erb :data, :layout => "template/default".to_sym
182
- end
183
- end
184
-
185
- get '/data/*' do |path|
186
- if path.empty?
187
- redirect '/data'
188
- else
189
- plugin,type,value = path.split("/")
190
- file = "#{settings.rrd_path}#{plugin}/#{type}.rrd"
191
- if File.exists? file
192
- data = Array.new
193
- # construct the RRD query
194
- function = 'AVERAGE'
195
- if params.has_key? 'function'
196
- param = params['function'].upcase
197
- puts param
198
- if %w(AVERAGE MIN MAX).include? param
199
- function = param
200
- end
201
- end
202
- command = "rrdtool fetch #{file} #{function}"
203
- command << " --end Now --start Now-#{params['last']}" if params.has_key? 'last'
204
- command << " --r #{params['resolution']}"if params.has_key? 'resolution'
205
- #$stderr.puts command if $DEBUG
206
- # get the data
207
- output = `#{command}`.split("\n")
208
- # select the value of interest
209
- headers = output.shift.split # remove header
210
- key = headers.index(value)
211
- output.delete_at 0 # remove empty line
212
- # collect the data
213
- output.each do |line|
214
- line = line.delete(':').split
215
- time = line[0].to_i
216
- value = line[key+1].to_f # omit time stamp
217
- lv = data[-1]
218
- data << [time, value]
219
- end
220
- #remove most time wrong elements
221
- data.slice!(0)
222
- data.slice!(-1)
223
- #filter values which are the same for multiple timestamps
224
- final_data = []
225
- data.each_index do |el|
226
- cur_e = data[el][1]
227
- if data[el-1] != nil and data[el+1] != nil and el != 0 and el != data.size-1
228
- last_e = data[el-1][1]
229
- next_e = data[el+1][1]
230
- if cur_e != last_e or cur_e != next_e then
231
- final_data.push(data[el])
232
- end
233
- else
234
- final_data.push(data[el])
235
- end
236
- end
237
- data = final_data
238
- if params.has_key? 'format'
239
- if params['format'] == 'json'
240
- content_type :json
241
- json = {'name'=>"#{plugin} #{file}",'data'=>data}
242
- JSON.pretty_generate json
243
- else
244
- content_type 'text/plain'
245
- output = String.new
246
- data.each { |line| output << "#{line[0]}: #{line[1..-1].join(' ')}\n" }
247
- output
248
- end
249
- else
250
- @data = data
251
- erb :show_values
252
- end
253
- end
254
- end
255
- end
256
-
257
- ##
258
- ## Web-Interface to all graphs generated from the Collectd RRD files
259
- ##
260
- get '/graph' do
261
- # Clients can discover a list of available graphs using the
262
- # format parameters
263
- if params.has_key? 'format'
264
- graph_list = settings.plugins.keys.sort.map! { |g| "/graph/#{g}" }
265
- if params['format'] == 'json'
266
- content_type :json
267
- JSON.pretty_generate graph_list
268
- else
269
- content_type :text
270
- graph_list.join("\n")
271
- end
272
- # By default the web-interface will be displayed
273
- else
274
- # by default each graph presents the last 12 hours
275
- unless params.has_key? 'start' and params.has_key? 'end'
276
- params['start'] = 'end-12h'
277
- params['end'] = 'now'
278
- end
279
- params['image'] = 'png' unless params.has_key?('image')
280
- # display only a subset of the graphs by default
281
- unless params.has_key?('display')
282
- params['display'] = [ 'cpus', 'memory', 'load', ]
283
- end
284
- # pass the list of graphs to display into the template
285
- @display = params['display']
286
- # remove it from the parameter list
287
- params.delete('display') if params.has_key?('display')
288
- # all other parameters will be appended for the graph
289
- # generation.
290
- p = Array.new; params.each_pair { |k,v| p << "#{k}=#{v}" }
291
- @args = p.join('&')
292
- # list of all available graphs for the drop down menu
293
- @graphs = settings.plugins
294
- # identifier for the template
295
- @target = 'graph'
296
- # render the templates
297
- erb :graph, :layout => "template/default".to_sym
298
- end
299
- end
300
-
301
- get '/graph/*' do |path|
302
- unless settings.plugins.has_key? path
303
- redirect '/'
304
- else
305
- @color = {
306
- :red_light => '#FF000044', :red_dark => '#FF0000AA',
307
- :green_light => '#00F00022', :green_dark => '#00F000AA',
308
- :yellow_light => '#FFFF0022', :yellow_dark => '#FFFF00AA',
309
- :blue_light => '#0000FF22', :blue_dark => '#0000FFAA',
310
- :orange_light => '#FF450022', :orange_dark => '#FF4500AA',
311
- :cyan_light => '#00FFFF22', :cyan_dark => '#00FFFFAA',
312
- :purple_light => '#FF00FF22', :purple_dark => '#FF00FFAA'
313
- }
314
- @type = params.has_key?('image') ? params['image'] : 'png'
315
- if params.has_key? 'start' and params.has_key? 'end'
316
- @start = params['start']
317
- @end = params['end']
318
- else
319
- @start = 'end-24h'
320
- @end = 'now'
321
- end
322
- @target = %Q[#{settings.public_folder}/images/#{path}.#{@type}]
323
- @rrd_path = settings.rrd_path
324
- command = erb "graphs/#{path}".to_sym, :layout => :graph_header
325
- puts command.chomp if $DEBUG
326
- output = `#{command} > /dev/null 2>&1`
327
- puts output.chomp if $DEBUG and not output.empty?
328
- redirect %Q[/images/#{path}.#{@type}]
329
- end
330
- end
331
-
332
- get '/report' do
333
- @reports = settings.reports.keys.sort
334
- # List all available path to reports
335
- if params.has_key? 'format'
336
- report_list = @reports.map! { |r| "/report/#{r}" }
337
- if params['format'] == 'json'
338
- content_type :json
339
- JSON.pretty_generate report_list
340
- else # default is plain text
341
- content_type :text
342
- report_list.join("\n")
343
- end
344
- # Render a HTML representation of all/a single report(s)
345
- else
346
- unless params.has_key? 'display'
347
- params['display'] = 'disk-free'
348
- end
349
- @display = params['display']
350
- @target = 'report'
351
- erb :report, :layout => "template/default".to_sym
352
- end
353
- end
354
-
355
- get '/report/*' do |path|
356
- redirect '/report' if path.empty?
357
- # user asks for a specific output format
358
- if params.has_key?('format')
359
- case params['format']
360
- when 'json'
361
- content_type :json
362
- @type = 'json'
363
- when 'html'
364
- @type = 'html'
365
- else
366
- content_type :plain
367
- @type = 'text'
368
- end
369
- else
370
- content_type :plain
371
- @type = 'text'
372
- end
373
- erb "reports/#{path}".to_sym
374
- end
375
-
376
- get '/' do
377
- redirect '/graph'
378
- end
379
-
380
- error 404 do
381
- redirect '/'
382
- end
383
-
384
- end
385
-
386
- #--------------------------------------------------------------
387
- # Main program
388
- #--------------------------------------------------------------
389
-
390
-
391
51
  begin
392
52
 
393
- unless options.log.nil?
394
- file = File.join(options.log,"#{exec_name}.log")
395
- $stdout.reopen(file, 'w')
53
+ Collectd::Interface::Options.help = help
54
+ Collectd::Interface::Options.parse
55
+ # drop the process PID into a file if the user asks for it
56
+ unless Collectd::Interface::Config.pid_file?
57
+ _pid_file = Collectd::Interface::Config.pid_file(exec_name)
58
+ File.open(_pid_file,'w') { |file| file.write "#{$$} " }
59
+ end
60
+ # pipe program output to a file if the users asks for it
61
+ unless Collectd::Interface::Config.log_file?
62
+ _log_file = Collectd::Interface::Config.log_file(exec_name)
63
+ $stdout.reopen(_log_file,'w')
396
64
  $stdout.sync = true
397
65
  $stderr.reopen($stdout)
398
66
  end
399
-
400
- unless options.pid_file.nil?
401
- File.open(File.join(options.pid_file,"#{exec_name}.pid"),'w') do |f|
402
- f.puts $$
403
- end
404
- end
405
-
406
- CollectdInterface.run!( :port => options.port )
67
+ Collectd::Interface::Service.run!
407
68
 
408
69
  rescue => exc
409
70
  $stderr.puts "ERROR: #{exc.message}"
410
71
  $stderr.puts " use -h for detailed instructions"
411
- if options.debug
72
+ if Collectd::Interface::Config.debug?
412
73
  $stderr.puts '-- Stack Trace --'
413
74
  $stderr.puts exc.backtrace
414
75
  else
@@ -417,4 +78,3 @@ rescue => exc
417
78
  exit 1
418
79
  end
419
80
 
420
- exit 0
@@ -3,20 +3,13 @@
3
3
  require 'fileutils'
4
4
  include FileUtils
5
5
 
6
- application_root = File.expand_path(File.join(File.dirname(File.expand_path(__FILE__)),'..'))
7
-
8
- name = File.basename(__FILE__)
9
-
10
- graphs = "#{application_root}/views/graphs"
11
- reports = "#{application_root}/views/reports"
12
-
13
6
  def list(path)
14
- Dir["#{path}/*"].sort.each do |file|
15
- name = File.basename(file)
7
+ Dir["#{path}/**/*"].sort.each do |file|
8
+ _name = file.gsub(%r<#{path}>,'')[1..-1]
16
9
  if /.*.erb.disabled$/ =~ file
17
- puts File.basename(name,'.erb.disabled') << ' (disabled)'
10
+ puts _name.gsub(/\.erb\.disabled/,'') << ' (disabled)'
18
11
  elsif /.*.erb$/ =~ file
19
- puts File.basename(name,'.erb')
12
+ puts _name.gsub(/\.erb/,'')
20
13
  end
21
14
  end
22
15
  end
@@ -49,6 +42,13 @@ def disable(component,name)
49
42
  end
50
43
  end
51
44
 
45
+ _root = File.expand_path(File.join(File.dirname(File.expand_path(__FILE__)),'..'))
46
+
47
+ name = File.basename(__FILE__)
48
+
49
+ graphs = "#{_root}/views/graphs"
50
+ reports = "#{_root}/views/reports"
51
+
52
52
  case ARGV[0]
53
53
  when 'graph'
54
54
  case ARGV[1]
@@ -0,0 +1,117 @@
1
+ require 'json'
2
+ require 'erb'
3
+ require 'singleton'
4
+ require 'forwardable'
5
+
6
+ module Collectd
7
+ module Interface
8
+ class Config
9
+ include Singleton
10
+ def initialize
11
+ @data = Hash.new
12
+ defaults()
13
+ find_graphs()
14
+ find_data()
15
+ find_reports()
16
+ end
17
+ # Hash like access to the internals
18
+ def [](key); @data[key] end
19
+ def []=(key,value); @data[key] = value end
20
+ # type casts & inspection
21
+ def to_json; JSON.pretty_generate(@data) end
22
+ def inspect
23
+ %Q[-- Config --\n#{self.to_json}\n------------]
24
+ end
25
+ # short cuts for attributes
26
+ def debug?; @data['debug'] end
27
+ # does the user wants to write a file containing the PID
28
+ def pid_file?
29
+ self['service']['pid_path'].empty?
30
+ end
31
+ def pid_file(name)
32
+ File.join(self['service']['pid_path'],"#{name}.pid")
33
+ end
34
+ def log_file?
35
+ self['service']['log_path'].empty?
36
+ end
37
+ def log_file(name)
38
+ File.join(self['service']['log_path'],"#{name}.log")
39
+ end
40
+ def root; @data['root'] end
41
+ class << self
42
+ extend Forwardable
43
+ def_delegators :instance, *Config.instance_methods(false)
44
+ end
45
+ private
46
+ def defaults
47
+ @data['debug'] = false
48
+ # defaults for the Sinatra application
49
+ @data['service'] = Hash.new
50
+ @data['service']['port'] = 5000
51
+ @data['service']['pid_path'] = String.new
52
+ @data['service']['log_path'] = String.new
53
+ @data['service']['config_path'] = String.new
54
+ # find the application root directory relative to this configuration file
55
+ @data['root'] = File.expand_path(File.join(File.dirname(File.expand_path(__FILE__)),'..','..','..'))
56
+ _hostname = `hostname -f`.chop
57
+ @data['rrd_path'] = File.join('/var/lib/collectd/rrd/',_hostname)
58
+ end
59
+ def find_graphs
60
+ self['graphs'] = Hash.new
61
+ # path to the plug-ins shipped with this software
62
+ _path = File.join(self.root,'views','graphs')
63
+ Dir["#{_path}/**/*.erb"].each do |file|
64
+ # strip the template suffix
65
+ _name = file.gsub(/\.erb/,'')
66
+ # remove the path to the graph template directory
67
+ _name = _name.gsub(%r<#{_path}>,'')[1..-1]
68
+ # strip the application path from the file name
69
+ _file = file.gsub(%r<#{_path}/>,'').gsub(/\.erb/,'')
70
+ # does the plug-in supports URI parameters
71
+ if _name.include?('/')
72
+ @config = true
73
+ @rrd_path = self['rrd_path']
74
+ _supports = JSON.parse(ERB.new(File.read(file)).result(binding))
75
+ _supports.each do |path|
76
+ self['graphs']["#{_name}/#{path}"] = _file
77
+ end
78
+ else
79
+ self['graphs'][_name] = _file
80
+ end
81
+ end
82
+ end
83
+ def find_data
84
+ self['data'] = Array.new
85
+ Dir["#{self['rrd_path']}/**/*.rrd"].each do |file|
86
+ plugin = file.gsub(%r<#{self['rrd_path']}>,'').split('/')[1]
87
+ rrd = File.basename(file,'.rrd')
88
+ `rrdtool info "#{file}"`.scan(%r{ds\[(\w*)\]}).uniq.flatten.each do |set|
89
+ self['data'] << "#{plugin}/#{rrd}/#{set}"
90
+ end
91
+ end
92
+ end
93
+ def find_reports
94
+ self['reports'] = Hash.new
95
+ _path = File.join(self.root,'views','reports')
96
+ Dir["#{_path}/**/*.erb"].each do |file|
97
+ # strip the template suffix
98
+ _name = file.gsub(/\.erb/,'')
99
+ # remove the path to the graph template directory
100
+ _name = _name.gsub(%r<#{_path}>,'')[1..-1]
101
+ # strip the application path from the file name
102
+ _file = file.gsub(%r<#{_path}/>,'').gsub(/\.erb/,'')
103
+ # path to the template file
104
+ if _name.include?('/')
105
+ @config = true
106
+ _supports = JSON.parse(ERB.new(File.read(file)).result(binding))
107
+ _supports.each do |path|
108
+ self['reports']["#{_name}/#{path}"] = _file
109
+ end
110
+ else
111
+ self['reports'][_name] = _file
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,68 @@
1
+ require 'getoptlong'
2
+ require 'singleton'
3
+ require 'forwardable'
4
+ require 'collectd/interface/config'
5
+
6
+ module Collectd
7
+ module Interface
8
+ class Options
9
+ include Singleton
10
+ attr_writer :help
11
+ def initialize
12
+ @help = String.new
13
+ @options = GetoptLong.new(
14
+ ['--debug','-d',GetoptLong::NO_ARGUMENT],
15
+ ['--help','-h',GetoptLong::NO_ARGUMENT],
16
+ ['--port','-p',GetoptLong::REQUIRED_ARGUMENT],
17
+ ['--log-file-path','-l',GetoptLong::REQUIRED_ARGUMENT],
18
+ ['--pid-file-path','-P',GetoptLong::REQUIRED_ARGUMENT],
19
+ ['--config-dump','-C',GetoptLong::NO_ARGUMENT],
20
+ ['--config','-c',GetoptLong::REQUIRED_ARGUMENT],
21
+ ['--version',GetoptLong::NO_ARGUMENT]
22
+ )
23
+ end
24
+ def parse
25
+ @options.each do |opt,arg|
26
+ case opt
27
+ when '--port'
28
+ Config['service']['port'] = arg.to_i
29
+ when '--log-file-path'
30
+ if File.directory? arg
31
+ Config['service']['log_path'] = arg
32
+ else
33
+ raise("#{arg} is not a directory!")
34
+ end
35
+ when '--pid-file-path'
36
+ if File.directory? arg
37
+ Config['service']['pid_path'] = arg
38
+ else
39
+ raise("#{arg} is not a directory!")
40
+ end
41
+ when '--config-dump'
42
+ $stdout.puts Config.inspect
43
+ exit 0
44
+ when '--debug'
45
+ Config['debug'] = true
46
+ when '--help'
47
+ $stdout.puts @help
48
+ exit 0
49
+ when '--config'
50
+ _config = File.expand_path(arg)
51
+ if File.directory? _config
52
+ Config['service']['config_path'] = _config
53
+ else
54
+ raise %Q[Configuration directory "#{config}" missing!]
55
+ end
56
+ when '--version'
57
+ $stdout.puts '0.5.0'
58
+ exit 0
59
+ end
60
+ end
61
+ end
62
+ class << self
63
+ extend Forwardable
64
+ def_delegators :instance, *Options.instance_methods(false)
65
+ end
66
+ end
67
+ end
68
+ end