collectd-rest-server 0.0.1

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