visage-app 1.0.0 → 2.0.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/.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
|