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.
- data/bin/collectd-interface-daemon +21 -361
- data/bin/collectd-interface-plugins +11 -11
- data/lib/collectd/interface/config.rb +117 -0
- data/lib/collectd/interface/options.rb +68 -0
- data/lib/collectd/interface/service/data.rb +110 -0
- data/lib/collectd/interface/service/graph.rb +106 -0
- data/lib/collectd/interface/service/report.rb +82 -0
- data/lib/collectd/interface/service.rb +44 -0
- data/public/images/cpus.png +0 -0
- data/public/images/device/ops/sda.png +0 -0
- data/public/images/device/ops/sda1.png +0 -0
- data/public/images/device/ops/sda2.png +0 -0
- data/public/images/device/ops/sda5.png +0 -0
- data/public/images/device/ops/sda6.png +0 -0
- data/public/images/device/ops/sda7.png +0 -0
- data/public/images/device/ops/sda8.png +0 -0
- data/public/images/device/ops/sda9.png +0 -0
- data/public/images/device/ops/sdb.png +0 -0
- data/public/images/device/ops/sdb1.png +0 -0
- data/public/images/device/ops/sdh.png +0 -0
- data/public/images/device/ops/sdh1.png +0 -0
- data/public/images/device/ops/sdh2.png +0 -0
- data/public/images/device/ops/sdh3.png +0 -0
- data/public/images/device/ops/sdh4.png +0 -0
- data/public/images/device/ops/sr0.png +0 -0
- data/public/images/device/time/sda.png +0 -0
- data/public/images/device/time/sda1.png +0 -0
- data/public/images/device/time/sda2.png +0 -0
- data/public/images/device/time/sda5.png +0 -0
- data/public/images/device/time/sda6.png +0 -0
- data/public/images/device/time/sda7.png +0 -0
- data/public/images/device/time/sda8.png +0 -0
- data/public/images/device/time/sda9.png +0 -0
- data/public/images/device/time/sdb.png +0 -0
- data/public/images/device/time/sdb1.png +0 -0
- data/public/images/device/time/sdh.png +0 -0
- data/public/images/device/time/sdh1.png +0 -0
- data/public/images/device/time/sdh2.png +0 -0
- data/public/images/device/time/sdh3.png +0 -0
- data/public/images/device/time/sdh4.png +0 -0
- data/public/images/device/time/sr0.png +0 -0
- data/public/images/device/traffic/sda.png +0 -0
- data/public/images/device/traffic/sda1.png +0 -0
- data/public/images/device/traffic/sda2.png +0 -0
- data/public/images/device/traffic/sda5.png +0 -0
- data/public/images/device/traffic/sda6.png +0 -0
- data/public/images/device/traffic/sda7.png +0 -0
- data/public/images/device/traffic/sda8.png +0 -0
- data/public/images/device/traffic/sda9.png +0 -0
- data/public/images/device/traffic/sdb.png +0 -0
- data/public/images/device/traffic/sdb1.png +0 -0
- data/public/images/device/traffic/sdh.png +0 -0
- data/public/images/device/traffic/sdh1.png +0 -0
- data/public/images/device/traffic/sdh2.png +0 -0
- data/public/images/device/traffic/sdh3.png +0 -0
- data/public/images/device/traffic/sdh4.png +0 -0
- data/public/images/device/traffic/sr0.png +0 -0
- data/public/images/irqs.png +0 -0
- data/public/images/load.png +0 -0
- data/public/images/memory.png +0 -0
- data/public/images/network/packets/eth0.png +0 -0
- data/public/images/network/packets/lo.png +0 -0
- data/public/images/network/packets/nbr0-nic.png +0 -0
- data/public/images/network/packets/nbr0.png +0 -0
- data/public/images/network/packets/virbr0.png +0 -0
- data/public/images/network/packets/vnet0.png +0 -0
- data/public/images/network/packets/vnet1.png +0 -0
- data/public/images/network/packets/vnet2.png +0 -0
- data/public/images/network/packets/vnet3.png +0 -0
- data/public/images/network/packets/vnet4.png +0 -0
- data/public/images/network/packets/vnet5.png +0 -0
- data/public/images/network/traffic/eth0.png +0 -0
- data/public/images/network/traffic/lo.png +0 -0
- data/public/images/network/traffic/nbr0-nic.png +0 -0
- data/public/images/network/traffic/nbr0.png +0 -0
- data/public/images/network/traffic/virbr0.png +0 -0
- data/public/images/network/traffic/vnet0.png +0 -0
- data/public/images/network/traffic/vnet1.png +0 -0
- data/public/images/network/traffic/vnet2.png +0 -0
- data/public/images/network/traffic/vnet3.png +0 -0
- data/public/images/network/traffic/vnet4.png +0 -0
- data/public/images/network/traffic/vnet5.png +0 -0
- data/public/images/processes.png +0 -0
- data/public/images/users.png +0 -0
- data/public/script/table_filter.js +1217 -0
- data/public/style/default.css +28 -0
- data/test/collectd/interface/config_test.rb +9 -0
- data/test/collectd/interface/service_config_test.rb +26 -0
- data/test/collectd/interface/service_data_test.rb +46 -0
- data/test/start.rb +3 -0
- data/views/graph_header.erb +13 -4
- data/views/graphs/device/ops.erb +37 -0
- data/views/graphs/device/time.erb +37 -0
- data/views/graphs/device/traffic.erb +38 -0
- data/views/graphs/{irq.erb.disabled → irqs.erb} +8 -8
- data/views/graphs/network/packets.erb +35 -0
- data/views/graphs/network/traffic.erb +35 -0
- data/views/graphs/{users.erb.disabled → users.erb} +0 -0
- data/views/report.erb +32 -14
- data/views/reports/open-files/local.erb +62 -0
- data/views/reports/{processes-cpu-usage.erb → processes.erb} +0 -0
- data/views/reports/{system-sockets.erb → sockets.erb} +0 -0
- data/views/reports/{disk-free.erb → storage.erb} +1 -1
- data/views/template/options/graph.erb +1 -1
- metadata +98 -30
- data/public/images/cpus.svg +0 -946
- data/public/images/disk-traffic-root.png +0 -0
- data/public/images/disk-traffic-srv.png +0 -0
- data/public/images/disk-traffic-tmp.png +0 -0
- data/public/images/disk-traffic-var.png +0 -0
- data/public/images/irq.png +0 -0
- data/public/images/load.svg +0 -638
- data/public/images/memory.svg +0 -741
- data/public/images/network-eth0.png +0 -0
- data/public/images/network-eth0.svg +0 -609
- data/public/images/network-lo.png +0 -0
- data/public/images/network-lo.svg +0 -644
- data/public/images/processes.svg +0 -832
- data/views/graphs/disk-traffic-root.erb +0 -24
- data/views/graphs/disk-traffic-srv.erb.disabled +0 -25
- data/views/graphs/disk-traffic-tmp.erb.disabled +0 -24
- data/views/graphs/disk-traffic-var.erb.disabled +0 -25
- data/views/graphs/network-eth0.erb +0 -23
- data/views/graphs/network-lo.erb +0 -23
- data/views/reports/list-open-files-lustre.erb.disabled +0 -44
- 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
|
-
|
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
|
-
--
|
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
|
-
|
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
|
-
|
394
|
-
|
395
|
-
|
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
|
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}
|
15
|
-
|
7
|
+
Dir["#{path}/**/*"].sort.each do |file|
|
8
|
+
_name = file.gsub(%r<#{path}>,'')[1..-1]
|
16
9
|
if /.*.erb.disabled$/ =~ file
|
17
|
-
puts
|
10
|
+
puts _name.gsub(/\.erb\.disabled/,'') << ' (disabled)'
|
18
11
|
elsif /.*.erb$/ =~ file
|
19
|
-
puts
|
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
|