visage-app 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +10 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile +1 -15
- data/Gemfile.lock +44 -42
- data/README.md +123 -49
- data/Rakefile +16 -26
- data/bin/visage-app +17 -4
- data/features/cli.feature +10 -3
- data/features/json.feature +37 -0
- data/features/step_definitions/{visage_steps.rb → cli_steps.rb} +1 -1
- data/features/step_definitions/json_steps.rb +50 -8
- data/features/step_definitions/site_steps.rb +1 -1
- data/features/support/config/default/profiles.yaml +335 -0
- data/features/{data → support}/config/with_no_profiles/.stub +0 -0
- data/features/support/config/with_no_profiles/profiles.yaml +0 -0
- data/features/support/config/with_old_profile_yaml/profiles.yaml +116 -0
- data/features/support/env.rb +2 -3
- data/lib/visage-app.rb +35 -25
- data/lib/visage-app/collectd/json.rb +115 -118
- data/lib/visage-app/collectd/rrds.rb +25 -19
- data/lib/visage-app/helpers.rb +17 -0
- data/lib/visage-app/profile.rb +18 -25
- data/lib/visage-app/public/images/caution.png +0 -0
- data/lib/visage-app/public/images/ok.png +0 -0
- data/lib/visage-app/public/images/questions.png +0 -0
- data/lib/visage-app/public/javascripts/builder.js +607 -0
- data/lib/visage-app/public/javascripts/graph.js +179 -142
- data/lib/visage-app/public/javascripts/message.js +520 -0
- data/lib/visage-app/public/javascripts/mootools-core-1.4.0-full-compat.js +6285 -0
- data/lib/visage-app/public/javascripts/mootools-more-1.4.0.1.js +6399 -0
- data/lib/visage-app/public/stylesheets/message.css +61 -0
- data/lib/visage-app/public/stylesheets/screen.css +149 -38
- data/lib/visage-app/version.rb +5 -0
- data/lib/visage-app/views/builder.haml +38 -49
- data/lib/visage-app/views/builder_form.haml +14 -0
- data/lib/visage-app/views/layout.haml +5 -2
- data/lib/visage-app/views/profile.haml +44 -25
- data/visage-app.gemspec +29 -132
- metadata +93 -150
- data/VERSION +0 -1
- data/features/builder.feature +0 -16
- data/lib/visage-app/collectd/profile.rb +0 -36
File without changes
|
File without changes
|
@@ -0,0 +1,116 @@
|
|
1
|
+
---
|
2
|
+
zend+tail+on+ubuntu:
|
3
|
+
:metrics: tail*/*
|
4
|
+
:hosts: ubuntu.localdomain
|
5
|
+
:url: zend+tail+on+ubuntu
|
6
|
+
:profile_name: zend tail on ubuntu
|
7
|
+
load+on+ubuntu+localdomain:
|
8
|
+
:metrics: load/*
|
9
|
+
:hosts: ubuntu.localdomain
|
10
|
+
:url: load+on+ubuntu+localdomain
|
11
|
+
:profile_name: load on ubuntu.localdomain
|
12
|
+
apache+on+blah:
|
13
|
+
:metrics: apache/*
|
14
|
+
:hosts: blah
|
15
|
+
:url: apache+on+blah
|
16
|
+
:profile_name: apache on blah
|
17
|
+
interfaces+on+blah:
|
18
|
+
:metrics: interface/*
|
19
|
+
:hosts: blah
|
20
|
+
:url: interfaces+on+blah
|
21
|
+
:profile_name: interfaces on blah
|
22
|
+
all+on+all:
|
23
|
+
:metrics: "*"
|
24
|
+
:hosts: "*"
|
25
|
+
:url: all+on+all
|
26
|
+
:profile_name: all on all
|
27
|
+
object+space+on+ree:
|
28
|
+
:metrics: curl_json-object_space/*
|
29
|
+
:hosts: ubuntu*
|
30
|
+
:url: object+space+on+ree
|
31
|
+
:profile_name: Object space on REE
|
32
|
+
disk+sda+on+ubuntu:
|
33
|
+
:metrics: disk-sda/*
|
34
|
+
:hosts: ubuntu.localdomain
|
35
|
+
:url: disk+sda+on+ubuntu
|
36
|
+
:profile_name: disk-sda on ubuntu
|
37
|
+
gc+on+ree:
|
38
|
+
:metrics: curl_json-gc/*
|
39
|
+
:hosts: ubuntu*
|
40
|
+
:url: gc+on+ree
|
41
|
+
:profile_name: GC on REE
|
42
|
+
gateway+ping+from+blah:
|
43
|
+
:metrics: ping/*
|
44
|
+
:hosts: blah
|
45
|
+
:url: gateway+ping+from+blah
|
46
|
+
:profile_name: gateway ping from blah
|
47
|
+
tcpconns+on+ubuntu:
|
48
|
+
:metrics: tcpconns*/*
|
49
|
+
:hosts: ubuntu*
|
50
|
+
:url: tcpconns+on+ubuntu
|
51
|
+
:profile_name: tcpconns on ubuntu
|
52
|
+
entropy+on+blah:
|
53
|
+
:metrics: entropy/*
|
54
|
+
:hosts: blah
|
55
|
+
:url: entropy+on+blah
|
56
|
+
:profile_name: entropy on blah
|
57
|
+
memory+on+ubunttu:
|
58
|
+
:metrics: memory/*
|
59
|
+
:hosts: ubuntu*
|
60
|
+
:url: memory+on+ubunttu
|
61
|
+
:profile_name: memory on ubunttu
|
62
|
+
processes+on+blah:
|
63
|
+
:metrics: processes/*
|
64
|
+
:hosts: blah
|
65
|
+
:url: processes+on+blah
|
66
|
+
:profile_name: processes on blah
|
67
|
+
ruby+gc+on+ubuntu+localdomain:
|
68
|
+
:metrics: curl_json-gc/*
|
69
|
+
:hosts: ubuntu*
|
70
|
+
:url: ruby+gc+on+ubuntu+localdomain
|
71
|
+
:profile_name: Ruby GC on ubuntu.localdomain
|
72
|
+
cpu+statistics+for+ubuntu+localdomain:
|
73
|
+
:metrics: cpu*/*
|
74
|
+
:hosts: ubuntu*
|
75
|
+
:url: cpu+statistics+for+ubuntu+localdomain
|
76
|
+
:profile_name: CPU statistics for ubuntu.localdomain
|
77
|
+
swap+on+blah:
|
78
|
+
:metrics: swap/*
|
79
|
+
:hosts: blah
|
80
|
+
:url: swap+on+blah
|
81
|
+
:profile_name: swap on blah
|
82
|
+
cpu+and+load+on+ubuntu:
|
83
|
+
:metrics: cpu*/*,load/*
|
84
|
+
:hosts: ubuntu*
|
85
|
+
:url: cpu+and+load+on+ubuntu
|
86
|
+
:profile_name: cpu and load on ubuntu
|
87
|
+
cpu+on+flapjack+workers:
|
88
|
+
:metrics: cpu*/*
|
89
|
+
:hosts: flapjack-*
|
90
|
+
:url: cpu+on+flapjack+workers
|
91
|
+
:profile_name: cpu on flapjack workers
|
92
|
+
memory+on+flapjack+workers+and+blah:
|
93
|
+
:metrics: memory/*
|
94
|
+
:hosts: flapjack-worker*,blah
|
95
|
+
:url: memory+on+flapjack+workers+and+blah
|
96
|
+
:profile_name: memory on flapjack workers and blah
|
97
|
+
uptime+on+blah:
|
98
|
+
:metrics: uptime/*
|
99
|
+
:hosts: blah
|
100
|
+
:url: uptime+on+blah
|
101
|
+
:profile_name: uptime on blah
|
102
|
+
users+on+blah:
|
103
|
+
:metrics: users/*
|
104
|
+
:hosts: blah
|
105
|
+
:url: users+on+blah
|
106
|
+
:profile_name: users on blah
|
107
|
+
irqs+on+blah:
|
108
|
+
:metrics: irq/*
|
109
|
+
:hosts: blah
|
110
|
+
:url: irqs+on+blah
|
111
|
+
:profile_name: irqs on blah
|
112
|
+
vmem+on+blah:
|
113
|
+
:metrics: vmem/*
|
114
|
+
:hosts: blah
|
115
|
+
:url: vmem+on+blah
|
116
|
+
:profile_name: vmem on blah
|
data/features/support/env.rb
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
require 'rubygems'
|
3
4
|
require 'pathname'
|
4
5
|
|
5
6
|
@root = Pathname.new(File.dirname(__FILE__)).parent.parent.expand_path
|
6
7
|
app_file = @root.join('lib/visage-app')
|
7
8
|
|
8
|
-
require 'rubygems'
|
9
|
-
require 'spec/expectations'
|
10
9
|
require 'rack/test'
|
11
10
|
require 'webrat'
|
12
11
|
|
13
|
-
ENV['CONFIG_PATH'] = @root.join('features/
|
12
|
+
ENV['CONFIG_PATH'] = @root.join('features/support/config/default')
|
14
13
|
|
15
14
|
require app_file
|
16
15
|
# Force the application name because polyglot breaks the auto-detection logic.
|
data/lib/visage-app.rb
CHANGED
@@ -18,20 +18,21 @@ require 'yajl/json_gem'
|
|
18
18
|
module Visage
|
19
19
|
class Application < Sinatra::Base
|
20
20
|
@root = Pathname.new(File.dirname(__FILE__)).parent.expand_path
|
21
|
-
set :
|
22
|
-
set :views,
|
21
|
+
set :public_folder, @root.join('lib/visage-app/public')
|
22
|
+
set :views, @root.join('lib/visage-app/views')
|
23
23
|
|
24
24
|
helpers Sinatra::LinkToHelper
|
25
25
|
helpers Sinatra::PageTitleHelper
|
26
|
+
helpers Sinatra::RequireJSHelper
|
26
27
|
|
27
28
|
configure do
|
28
29
|
Visage::Config.use do |c|
|
29
30
|
# FIXME: make this configurable through file
|
30
31
|
c['rrddir'] = ENV["RRDDIR"] ? Pathname.new(ENV["RRDDIR"]).expand_path : Pathname.new("/var/lib/collectd/rrd").expand_path
|
31
|
-
c['types'] = ENV["TYPES"]
|
32
|
+
c['types'] = ENV["TYPES"] ? Visage::Types.new(:filename => ENV["TYPES"]) : Visage::Types.new
|
32
33
|
end
|
33
34
|
|
34
|
-
# Load up the
|
35
|
+
# Load up the profiles.yaml. Creates it if it doesn't already exist.
|
35
36
|
Visage::Profile.load
|
36
37
|
end
|
37
38
|
end
|
@@ -44,9 +45,6 @@ module Visage
|
|
44
45
|
get '/profiles/:url' do
|
45
46
|
@profile = Visage::Profile.get(params[:url])
|
46
47
|
raise Sinatra::NotFound unless @profile
|
47
|
-
@start = params[:start]
|
48
|
-
@finish = params[:finish]
|
49
|
-
@live = params[:live] ? true : false
|
50
48
|
haml :profile
|
51
49
|
end
|
52
50
|
|
@@ -59,6 +57,17 @@ module Visage
|
|
59
57
|
|
60
58
|
class Builder < Application
|
61
59
|
|
60
|
+
post '/builder' do
|
61
|
+
@profile = Visage::Profile.new(params)
|
62
|
+
|
63
|
+
if @profile.save
|
64
|
+
{'status' => 'ok'}.to_json
|
65
|
+
else
|
66
|
+
status 400 # Bad Request
|
67
|
+
{'status' => 'error', 'errors' => @profile.errors}.to_json
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
62
71
|
get "/builder" do
|
63
72
|
if params[:submit] == "create"
|
64
73
|
@profile = Visage::Profile.new(params)
|
@@ -75,7 +84,7 @@ module Visage
|
|
75
84
|
end
|
76
85
|
end
|
77
86
|
|
78
|
-
#
|
87
|
+
# Infrastructure for embedding.
|
79
88
|
get '/javascripts/visage.js' do
|
80
89
|
javascript = ""
|
81
90
|
%w{raphael-min g.raphael g.line mootools-1.2.3-core mootools-1.2.3.1-more graph}.each do |js|
|
@@ -98,27 +107,28 @@ module Visage
|
|
98
107
|
|
99
108
|
# /data/:host/:plugin/:optional_plugin_instance
|
100
109
|
get %r{/data/([^/]+)/([^/]+)((/[^/]+)*)} do
|
101
|
-
host
|
102
|
-
plugin
|
103
|
-
|
104
|
-
start
|
105
|
-
finish
|
106
|
-
|
107
|
-
collectd =
|
108
|
-
json = collectd.json(:host
|
109
|
-
:plugin
|
110
|
-
:
|
111
|
-
:start
|
112
|
-
:finish
|
113
|
-
|
110
|
+
host = params[:captures][0].gsub("\0", "")
|
111
|
+
plugin = params[:captures][1].gsub("\0", "")
|
112
|
+
instances = params[:captures][2].gsub("\0", "")
|
113
|
+
start = params[:start]
|
114
|
+
finish = params[:finish]
|
115
|
+
|
116
|
+
collectd = Visage::Collectd::JSON.new(:rrddir => Visage::Config.rrddir)
|
117
|
+
json = collectd.json(:host => host,
|
118
|
+
:plugin => plugin,
|
119
|
+
:instances => instances,
|
120
|
+
:start => start,
|
121
|
+
:finish => finish)
|
122
|
+
|
123
|
+
# If the request is cross-domain, we need to serve JSON-P.
|
114
124
|
maybe_wrap_with_callback(json)
|
115
125
|
end
|
116
126
|
|
117
127
|
get %r{/data/([^/]+)} do
|
118
|
-
|
119
|
-
metrics = Visage::Collectd::RRDs.metrics(:
|
128
|
+
hosts = params[:captures][0].gsub("\0", "")
|
129
|
+
metrics = Visage::Collectd::RRDs.metrics(:hosts => hosts)
|
120
130
|
|
121
|
-
json = {
|
131
|
+
json = { :metrics => metrics }.to_json
|
122
132
|
maybe_wrap_with_callback(json)
|
123
133
|
end
|
124
134
|
|
@@ -128,7 +138,7 @@ module Visage
|
|
128
138
|
maybe_wrap_with_callback(json)
|
129
139
|
end
|
130
140
|
|
131
|
-
#
|
141
|
+
# Wraps json with a callback method that JSON-P clients can call.
|
132
142
|
def maybe_wrap_with_callback(json)
|
133
143
|
params[:callback] ? params[:callback] + '(' + json + ')' : json
|
134
144
|
end
|
@@ -10,133 +10,130 @@ require 'yajl'
|
|
10
10
|
#
|
11
11
|
# A loose shim onto RRDtool, with some extra logic to normalise the data.
|
12
12
|
#
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
@rrddir = opts[:rrddir] || CollectdJSON.rrddir
|
17
|
-
@types = opts[:types] || CollectdJSON.types
|
18
|
-
end
|
19
|
-
|
20
|
-
# Entry point.
|
21
|
-
def json(opts={})
|
22
|
-
host = opts[:host]
|
23
|
-
plugin = opts[:plugin]
|
24
|
-
plugin_instances = opts[:plugin_instances][/\w.*/]
|
25
|
-
instances = plugin_instances.blank? ? '*' : '{' + plugin_instances.split('/').join(',') + '}'
|
26
|
-
rrdglob = "#{@rrddir}/#{host}/#{plugin}/#{instances}.rrd"
|
27
|
-
|
28
|
-
start = case
|
29
|
-
when opts[:start] && opts[:start].index('.')
|
30
|
-
opts[:start].split('.').first
|
31
|
-
when opts[:start]
|
32
|
-
opts[:start]
|
33
|
-
else
|
34
|
-
(Time.now - 3600).to_i
|
35
|
-
end
|
36
|
-
|
37
|
-
finish = case
|
38
|
-
when opts[:finish] && opts[:finish].index('.')
|
39
|
-
opts[:finish].split('.').first
|
40
|
-
when opts[:finish]
|
41
|
-
opts[:finish]
|
42
|
-
else
|
43
|
-
Time.now.to_i
|
44
|
-
end
|
45
|
-
|
46
|
-
data = []
|
47
|
-
|
48
|
-
Dir.glob(rrdglob).map do |rrdname|
|
49
|
-
parts = rrdname.gsub(/#{@rrddir}\//, '').split('/')
|
50
|
-
host_name = parts[0]
|
51
|
-
plugin_name = parts[1]
|
52
|
-
instance_name = File.basename(parts[2], '.rrd')
|
53
|
-
rrd = Errand.new(:filename => rrdname)
|
54
|
-
|
55
|
-
|
56
|
-
data << { :plugin => plugin_name, :instance => instance_name,
|
57
|
-
:host => host_name,
|
58
|
-
:start => start,
|
59
|
-
:finish => finish,
|
60
|
-
:rrd => rrd }
|
61
|
-
end
|
62
|
-
|
63
|
-
encode(data)
|
64
|
-
end
|
65
|
-
|
66
|
-
private
|
67
|
-
# Attempt to structure the JSON reasonably sanely, so the consumer (i.e. a
|
68
|
-
# browser) doesn't have to do a lot of computationally expensive work.
|
69
|
-
def encode(datas)
|
70
|
-
|
71
|
-
structure = {}
|
72
|
-
datas.each do |data|
|
73
|
-
fetch = data[:rrd].fetch(:function => "AVERAGE",
|
74
|
-
:start => data[:start],
|
75
|
-
:finish => data[:finish])
|
76
|
-
rrd_data = fetch[:data]
|
77
|
-
|
78
|
-
# A single rrd can have multiple data sets (multiple metrics within
|
79
|
-
# the same file). Separate the metrics.
|
80
|
-
rrd_data.each_pair do |source, metric|
|
81
|
-
|
82
|
-
# Filter out NaNs and weirdly massive values so yajl doesn't choke
|
83
|
-
metric.map! do |datapoint|
|
84
|
-
case
|
85
|
-
when datapoint && datapoint.nan?
|
86
|
-
@tripped = true
|
87
|
-
@last_valid
|
88
|
-
when @tripped
|
89
|
-
@last_valid
|
90
|
-
else
|
91
|
-
@last_valid = datapoint
|
92
|
-
end
|
93
|
-
end
|
13
|
+
module Visage
|
14
|
+
module Collectd
|
15
|
+
class JSON
|
94
16
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
instance = data[:instance]
|
100
|
-
start = data[:start].to_i
|
101
|
-
finish = data[:finish].to_i
|
102
|
-
|
103
|
-
structure[host] ||= {}
|
104
|
-
structure[host][plugin] ||= {}
|
105
|
-
structure[host][plugin][instance] ||= {}
|
106
|
-
structure[host][plugin][instance][source] ||= {}
|
107
|
-
structure[host][plugin][instance][source][:start] ||= start
|
108
|
-
structure[host][plugin][instance][source][:finish] ||= finish
|
109
|
-
structure[host][plugin][instance][source][:data] ||= metric
|
17
|
+
def initialize(opts={})
|
18
|
+
@rrddir = opts[:rrddir] || Visage::Collectd::JSON.rrddir
|
19
|
+
@types = opts[:types] || Visage::Collectd::JSON.types
|
20
|
+
end
|
110
21
|
|
22
|
+
def parse_time(time, opts={})
|
23
|
+
case
|
24
|
+
when time && time.index('.')
|
25
|
+
time.split('.').first.to_i
|
26
|
+
when time
|
27
|
+
time.to_i
|
28
|
+
else
|
29
|
+
opts[:default] || Time.now.to_i
|
30
|
+
end
|
111
31
|
end
|
112
|
-
end
|
113
32
|
|
114
|
-
|
115
|
-
|
116
|
-
|
33
|
+
# Entry point.
|
34
|
+
def json(opts={})
|
35
|
+
host = opts[:host]
|
36
|
+
plugin = opts[:plugin]
|
37
|
+
instances = opts[:instances][/\w.*/]
|
38
|
+
instances = instances.blank? ? '*' : '{' + instances.split('/').join(',') + '}'
|
39
|
+
rrdglob = "#{@rrddir}/#{host}/#{plugin}/#{instances}.rrd"
|
40
|
+
finish = parse_time(opts[:finish])
|
41
|
+
start = parse_time(opts[:start], :default => (finish - 3600 || (Time.now - 3600).to_i))
|
42
|
+
data = []
|
43
|
+
|
44
|
+
Dir.glob(rrdglob).map do |rrdname|
|
45
|
+
parts = rrdname.gsub(/#{@rrddir}\//, '').split('/')
|
46
|
+
host_name = parts[0]
|
47
|
+
plugin_name = parts[1]
|
48
|
+
instance_name = File.basename(parts[2], '.rrd')
|
49
|
+
rrd = Errand.new(:filename => rrdname)
|
50
|
+
|
51
|
+
data << { :plugin => plugin_name, :instance => instance_name,
|
52
|
+
:host => host_name,
|
53
|
+
:start => start,
|
54
|
+
:finish => finish,
|
55
|
+
:rrd => rrd }
|
56
|
+
end
|
117
57
|
|
118
|
-
|
119
|
-
|
58
|
+
encode(data)
|
59
|
+
end
|
120
60
|
|
121
|
-
|
122
|
-
|
123
|
-
|
61
|
+
private
|
62
|
+
# Attempt to structure the JSON reasonably sanely, so the consumer (i.e. a
|
63
|
+
# browser) doesn't have to do a lot of computationally expensive work.
|
64
|
+
def encode(datas)
|
65
|
+
|
66
|
+
structure = {}
|
67
|
+
datas.each do |data|
|
68
|
+
fetch = data[:rrd].fetch(:function => "AVERAGE",
|
69
|
+
:start => data[:start],
|
70
|
+
:finish => data[:finish])
|
71
|
+
rrd_data = fetch[:data]
|
72
|
+
|
73
|
+
# A single rrd can have multiple data sets (multiple metrics within
|
74
|
+
# the same file). Separate the metrics.
|
75
|
+
rrd_data.each_pair do |source, metric|
|
76
|
+
|
77
|
+
# Filter out NaNs and weirdly massive values so yajl doesn't choke
|
78
|
+
metric.map! do |datapoint|
|
79
|
+
case
|
80
|
+
when datapoint && datapoint.nan?
|
81
|
+
@tripped = true
|
82
|
+
@last_valid
|
83
|
+
when @tripped
|
84
|
+
@last_valid
|
85
|
+
else
|
86
|
+
@last_valid = datapoint
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Last value is always wack. Set to 0, so the timescale isn't off by 1.
|
91
|
+
metric[-1] = 0.0
|
92
|
+
host = data[:host]
|
93
|
+
plugin = data[:plugin]
|
94
|
+
instance = data[:instance]
|
95
|
+
start = data[:start].to_i
|
96
|
+
finish = data[:finish].to_i
|
97
|
+
|
98
|
+
structure[host] ||= {}
|
99
|
+
structure[host][plugin] ||= {}
|
100
|
+
structure[host][plugin][instance] ||= {}
|
101
|
+
structure[host][plugin][instance][source] ||= {}
|
102
|
+
structure[host][plugin][instance][source][:start] ||= start
|
103
|
+
structure[host][plugin][instance][source][:finish] ||= finish
|
104
|
+
structure[host][plugin][instance][source][:data] ||= metric
|
124
105
|
|
125
|
-
|
126
|
-
|
127
|
-
end
|
106
|
+
end
|
107
|
+
end
|
128
108
|
|
129
|
-
|
130
|
-
|
131
|
-
Dir.glob("#{@rrddir}/*").map {|e| e.split('/').last }.sort
|
109
|
+
encoder = Yajl::Encoder.new
|
110
|
+
encoder.encode(structure)
|
132
111
|
end
|
133
|
-
end
|
134
112
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
113
|
+
class << self
|
114
|
+
attr_writer :rrddir
|
115
|
+
|
116
|
+
def rrddir
|
117
|
+
@rrddir ||= Visage::Config.rrddir
|
118
|
+
end
|
119
|
+
|
120
|
+
def types
|
121
|
+
@types ||= Visage::Config.types
|
122
|
+
end
|
139
123
|
|
140
|
-
|
124
|
+
def hosts
|
125
|
+
if @rrddir
|
126
|
+
Dir.glob("#{@rrddir}/*").map {|e| e.split('/').last }.sort
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def plugins(opts={})
|
131
|
+
host = opts[:host] || '*'
|
132
|
+
Dir.glob("#{@rrddir}/#{host}/*").map {|e| e.split('/').last }.sort
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
141
136
|
|
142
|
-
end
|
137
|
+
end # class JSON
|
138
|
+
end # module Collectd
|
139
|
+
end # module Visage
|