collectd-interface 0.3.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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