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.
- 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
|