collectd-rest-server 0.0.1
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-rest-server +334 -0
- data/init.d/collectd-rest-server +50 -0
- metadata +66 -0
@@ -0,0 +1,334 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#---------------------------------------------------------------
|
4
|
+
#
|
5
|
+
# REST interface to the RRD stored by 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 2011
|
26
|
+
# Version: 0.0.1
|
27
|
+
#----------------------------------------------------------------
|
28
|
+
|
29
|
+
|
30
|
+
require 'rubygems'
|
31
|
+
require 'json'
|
32
|
+
require 'getoptlong'
|
33
|
+
require 'ostruct'
|
34
|
+
|
35
|
+
exec_name = File.split(__FILE__)[-1]
|
36
|
+
|
37
|
+
help = <<-EOF
|
38
|
+
Synopsis
|
39
|
+
========
|
40
|
+
|
41
|
+
#{exec_name}: Run a REST interface to your local Collectd.
|
42
|
+
|
43
|
+
Usage
|
44
|
+
-----
|
45
|
+
|
46
|
+
#{exec_name} [OPTIONS]
|
47
|
+
|
48
|
+
OPTIONS: see below.
|
49
|
+
|
50
|
+
Options
|
51
|
+
-------
|
52
|
+
|
53
|
+
--help,-h:
|
54
|
+
Show this help information.
|
55
|
+
--debug, -d:
|
56
|
+
More verbose output while running this.
|
57
|
+
--port,-p NUMBER:
|
58
|
+
Start the REST server at port NUMBER.
|
59
|
+
--log,-l PATH:
|
60
|
+
Write the log output to PATH.
|
61
|
+
--files, -f PATH:
|
62
|
+
RRD files are located in PATH.
|
63
|
+
Default is /var/lib/collectd/rrd
|
64
|
+
--pid-file, -P PATH:
|
65
|
+
Write the process ID to a file in PATH.
|
66
|
+
|
67
|
+
Examples
|
68
|
+
--------
|
69
|
+
|
70
|
+
Run the stuff in debug-mode:
|
71
|
+
|
72
|
+
#{exec_name} -d
|
73
|
+
|
74
|
+
Start the server as root service:
|
75
|
+
|
76
|
+
|
77
|
+
#{exec_name} -p 5000 -l /var/log/ -P /var/run/
|
78
|
+
|
79
|
+
REST
|
80
|
+
====
|
81
|
+
|
82
|
+
List all available values:
|
83
|
+
|
84
|
+
http://../list-values
|
85
|
+
|
86
|
+
Get a the time-series of a specific value.
|
87
|
+
|
88
|
+
http://../interface/if_packets-eth0/rx
|
89
|
+
|
90
|
+
By default the severs will answer with averages
|
91
|
+
of all data-points and an HTML document.
|
92
|
+
|
93
|
+
Parameters
|
94
|
+
----------
|
95
|
+
|
96
|
+
http://../value?last=36h&resolution=3600
|
97
|
+
|
98
|
+
Request only values from the last 36 hours, with a
|
99
|
+
data point resolution of 1 hour (3600 seconds).
|
100
|
+
Default resolution is the highest possible and you
|
101
|
+
will get by default values of the last 24 hours.
|
102
|
+
In the time specification you can also use (w)eeks,
|
103
|
+
(d)ays and (m)inutes.
|
104
|
+
|
105
|
+
http://../rx?function=max&format=json
|
106
|
+
|
107
|
+
Request the maximum of all data points for the past
|
108
|
+
in the JSON format. Other consolidation functions
|
109
|
+
are average (default) and min, for the smallest value
|
110
|
+
in a time-frame.
|
111
|
+
|
112
|
+
http://../value?callback=parseResponse
|
113
|
+
|
114
|
+
The server then wraps its JSON response with this
|
115
|
+
prefix, or "padding", before sending it to the browser.
|
116
|
+
|
117
|
+
EOF
|
118
|
+
|
119
|
+
options = OpenStruct.new
|
120
|
+
options.debug = false
|
121
|
+
options.port = 4567
|
122
|
+
options.log = nil
|
123
|
+
options.files = '/var/lib/collectd/rrd'
|
124
|
+
options.pid_file = nil
|
125
|
+
|
126
|
+
GetoptLong.new(
|
127
|
+
['--help','-h',GetoptLong::NO_ARGUMENT],
|
128
|
+
['--debug','-d',GetoptLong::NO_ARGUMENT],
|
129
|
+
['--port','-p',GetoptLong::REQUIRED_ARGUMENT],
|
130
|
+
['--log','-l',GetoptLong::REQUIRED_ARGUMENT],
|
131
|
+
['--files','-f',GetoptLong::REQUIRED_ARGUMENT],
|
132
|
+
['--pid-file','-P',GetoptLong::REQUIRED_ARGUMENT]
|
133
|
+
).each do |opt,arg|
|
134
|
+
case opt
|
135
|
+
when '--port'
|
136
|
+
options.port = arg
|
137
|
+
when '--log'
|
138
|
+
options.log = arg
|
139
|
+
when '--files'
|
140
|
+
options.files = arg
|
141
|
+
when '--pid-file'
|
142
|
+
options.pid_file = arg
|
143
|
+
when '--debug'
|
144
|
+
options.debug = true
|
145
|
+
$DEBUG = true
|
146
|
+
when '--help'
|
147
|
+
$stdout.puts help
|
148
|
+
exit 0
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Didn't found a better way to pass configuration to the Sinatra instance
|
153
|
+
ENV['COLLECTD_RRD_FILES'] = options.files
|
154
|
+
|
155
|
+
# Make sure to parse ARGV before loading Sinatra!
|
156
|
+
require 'sinatra'
|
157
|
+
|
158
|
+
#---------------------------------------------------------------
|
159
|
+
# REST provider
|
160
|
+
#---------------------------------------------------------------
|
161
|
+
|
162
|
+
class Collecter < Sinatra::Base
|
163
|
+
|
164
|
+
configure do
|
165
|
+
mime_type :json, 'application/json' # JSON as supported output format
|
166
|
+
enable :inline_templates # HTML templates at the bottom of this file
|
167
|
+
enable :run # this will be executed by wrapper scripts
|
168
|
+
hostname = `hostname -f`.chop
|
169
|
+
set :path, "#{ENV['COLLECTD_RRD_FILES']}/#{hostname}/"
|
170
|
+
# list available RRD databases of this host
|
171
|
+
values = Array.new
|
172
|
+
Dir["#{settings.path}/**/*.rrd"].each do |file|
|
173
|
+
plugin = file.gsub(%r<#{settings.path}>,'').split('/')[1]
|
174
|
+
rrd = File.basename(file,'.rrd')
|
175
|
+
`rrdtool info #{file}`.scan(%r{ds\[(\w*)\]}).uniq.flatten.each do |set|
|
176
|
+
values << "#{plugin}/#{rrd}/#{set}"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
set :values, values
|
180
|
+
end
|
181
|
+
|
182
|
+
get '/list-values' do
|
183
|
+
if params.has_key? 'callback'
|
184
|
+
content_type :js
|
185
|
+
json = JSON.pretty_generate settings.values
|
186
|
+
response = "#{params['callback']}(#{json})"
|
187
|
+
response
|
188
|
+
elsif params['format'] == 'json'
|
189
|
+
content_type :json
|
190
|
+
JSON.pretty_generate settings.values
|
191
|
+
else
|
192
|
+
@data = settings.values
|
193
|
+
erb :values
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
get '/*' do |path|
|
198
|
+
redirect '/list-values' unless settings.values.include?(path)
|
199
|
+
plugin,type,value = path.split("/")
|
200
|
+
file = "#{settings.path}/#{plugin}/#{type}.rrd"
|
201
|
+
if File.exists? file
|
202
|
+
data = Array.new
|
203
|
+
# construct the RRD query
|
204
|
+
function = 'AVERAGE'
|
205
|
+
if params.has_key? 'function'
|
206
|
+
param = params['function'].upcase
|
207
|
+
puts param
|
208
|
+
if %w(AVERAGE MIN MAX).include? param
|
209
|
+
function = param
|
210
|
+
end
|
211
|
+
end
|
212
|
+
command = "rrdtool fetch #{file} #{function}"
|
213
|
+
command << " --end Now --start Now-#{params['last']}" if params.has_key? 'last'
|
214
|
+
command << " --r #{params['resolution']}"if params.has_key? 'resolution'
|
215
|
+
$stderr.puts command if $DEBUG
|
216
|
+
# get the data
|
217
|
+
output = `#{command}`.split("\n")
|
218
|
+
# select the value of interest
|
219
|
+
headers = output.shift.split # remove header
|
220
|
+
key = headers.index(value)
|
221
|
+
output.delete_at 0 # remove empty line
|
222
|
+
# collect the data
|
223
|
+
output.each do |line|
|
224
|
+
line = line.delete(':').split
|
225
|
+
time = line[0].to_i
|
226
|
+
value = line[key+1].to_f # omit time stamp
|
227
|
+
lv = data[-1]
|
228
|
+
data << [time, value]
|
229
|
+
end
|
230
|
+
#remove most time wrong elements
|
231
|
+
data.slice!(0)
|
232
|
+
data.slice!(-1)
|
233
|
+
#filter values which are the same for multiple timestamps
|
234
|
+
final_data = []
|
235
|
+
data.each_index do |el|
|
236
|
+
cur_e = data[el][1]
|
237
|
+
if data[el-1] != nil and data[el+1] != nil and el != 0 and el != data.size-1
|
238
|
+
last_e = data[el-1][1]
|
239
|
+
next_e = data[el+1][1]
|
240
|
+
if cur_e != last_e or cur_e != next_e then
|
241
|
+
final_data.push(data[el])
|
242
|
+
end
|
243
|
+
else
|
244
|
+
final_data.push(data[el])
|
245
|
+
end
|
246
|
+
end
|
247
|
+
data = final_data
|
248
|
+
if params.has_key? 'callback'
|
249
|
+
content_type :js
|
250
|
+
json = {'name'=>"#{plugin} #{type}",'type'=>'spline' ,'data'=>data, 'stack'=>"#{plugin}"}
|
251
|
+
json = JSON.pretty_generate json
|
252
|
+
response = "#{params['callback']}(#{json})"
|
253
|
+
response
|
254
|
+
elsif params['format'] == 'json'
|
255
|
+
content_type :json
|
256
|
+
json = {'name'=>"#{plugin} #{file}",'type'=>'spline' ,'data'=>data, 'stack'=>"#{plugin}"}
|
257
|
+
JSON.pretty_generate json
|
258
|
+
else
|
259
|
+
@data = data
|
260
|
+
erb :data
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
error do
|
266
|
+
flash[:error] = env['sinatra.error'].to_s
|
267
|
+
redirect '/list-values'
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
#--------------------------------------------------------------
|
272
|
+
# Main program
|
273
|
+
#--------------------------------------------------------------
|
274
|
+
|
275
|
+
|
276
|
+
begin
|
277
|
+
|
278
|
+
unless options.log.nil?
|
279
|
+
file = File.join(options.log,"#{exec_name}.log")
|
280
|
+
$stdout.reopen(file, 'w')
|
281
|
+
$stdout.sync = true
|
282
|
+
$stderr.reopen($stdout)
|
283
|
+
end
|
284
|
+
|
285
|
+
unless options.pid_file.nil?
|
286
|
+
File.open(File.join(options.pid_file,"#{exec_name}.pid"),'w') do |f|
|
287
|
+
f.puts $$
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
# start the REST interface
|
292
|
+
Collecter.run!( :port => options.port )
|
293
|
+
|
294
|
+
rescue => exc
|
295
|
+
$stderr.puts "ERROR: #{exc.message}"
|
296
|
+
$stderr.puts " use -h for detailed instructions"
|
297
|
+
if options.debug
|
298
|
+
$stderr.puts '-- Stack Trace --'
|
299
|
+
$stderr.puts exc.backtrace
|
300
|
+
else
|
301
|
+
$stderr.puts 'You may want run this in debug mode with \'-d\''
|
302
|
+
end
|
303
|
+
exit 1
|
304
|
+
end
|
305
|
+
|
306
|
+
exit 0
|
307
|
+
|
308
|
+
#--------------------------------------------------------
|
309
|
+
# Templates for the REST provider
|
310
|
+
#--------------------------------------------------------
|
311
|
+
|
312
|
+
__END__
|
313
|
+
@@ layout
|
314
|
+
<html>
|
315
|
+
<body>
|
316
|
+
<%= yield %>
|
317
|
+
</body>
|
318
|
+
</html>
|
319
|
+
|
320
|
+
@@ values
|
321
|
+
<ul style='list-style-type:none'>
|
322
|
+
<% for val in @data %>
|
323
|
+
<li><%= val %></li>
|
324
|
+
<% end %>
|
325
|
+
</ul>
|
326
|
+
|
327
|
+
@@ data
|
328
|
+
<table>
|
329
|
+
<% for entry in @data %>
|
330
|
+
<tr><td><%= entry[0] %></td><td><%= entry[1]%></td></tr>
|
331
|
+
<% end %>
|
332
|
+
</table>
|
333
|
+
|
334
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
HOME=/opt/collectd
|
4
|
+
PORT=5000
|
5
|
+
LOG_PATH=/var/log
|
6
|
+
PID_PATH=/var/run
|
7
|
+
TIME=`date +'%Y-%m-%d %H:%M'`
|
8
|
+
|
9
|
+
PID=-1 # non existing
|
10
|
+
PID_FILE=$PID_PATH/collectd-rest-server.pid
|
11
|
+
if [ -e $PID_FILE ]; then
|
12
|
+
PID=$(cat $PID_FILE)
|
13
|
+
fi
|
14
|
+
|
15
|
+
start() {
|
16
|
+
$HOME/collectd-rest-server -p $PORT -l $LOG_PATH -P $PID_PATH &
|
17
|
+
}
|
18
|
+
stop() {
|
19
|
+
if ps -p $PID > /dev/null 2>&1; then
|
20
|
+
kill -TERM ${PID}
|
21
|
+
fi
|
22
|
+
}
|
23
|
+
|
24
|
+
case "$1" in
|
25
|
+
start)
|
26
|
+
start
|
27
|
+
;;
|
28
|
+
stop)
|
29
|
+
stop
|
30
|
+
rm $PID_FILE
|
31
|
+
;;
|
32
|
+
restart)
|
33
|
+
stop
|
34
|
+
sleep 2
|
35
|
+
start
|
36
|
+
;;
|
37
|
+
status)
|
38
|
+
if [ -e /proc/${PID} -a /proc/${PID}/exe ]; then
|
39
|
+
exit 0
|
40
|
+
else
|
41
|
+
exit 1
|
42
|
+
fi
|
43
|
+
;;
|
44
|
+
*)
|
45
|
+
echo "Usage: $0 start|stop|restart|status"
|
46
|
+
exit 1
|
47
|
+
;;
|
48
|
+
esac
|
49
|
+
|
50
|
+
exit 0
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: collectd-rest-server
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Victor Penso
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-09-01 00:00:00.000000000 +02:00
|
13
|
+
default_executable: collectd-rest-server
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: sinatra
|
17
|
+
requirement: &19329200 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.2.6
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *19329200
|
26
|
+
description: ''
|
27
|
+
email: v.penso@gsi.de
|
28
|
+
executables:
|
29
|
+
- collectd-rest-server
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- init.d/collectd-rest-server
|
34
|
+
- bin/collectd-rest-server
|
35
|
+
has_rdoc: true
|
36
|
+
homepage: ''
|
37
|
+
licenses:
|
38
|
+
- GPLv3
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options:
|
41
|
+
- --line-numbers
|
42
|
+
- --inline-source
|
43
|
+
- --title
|
44
|
+
- Collectd-Rest-Server
|
45
|
+
require_paths:
|
46
|
+
- ''
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
none: false
|
49
|
+
requirements:
|
50
|
+
- - ! '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ! '>='
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
requirements:
|
60
|
+
- Collectd (http://collectd.org)
|
61
|
+
rubyforge_project: collectd-rest-server
|
62
|
+
rubygems_version: 1.6.2
|
63
|
+
signing_key:
|
64
|
+
specification_version: 3
|
65
|
+
summary: Servers a REST interface exposing data from RRD files stored by Collectd
|
66
|
+
test_files: []
|