visage-app 0.2.7 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -8,3 +8,4 @@ webrat*
8
8
  visage-app.gemspec
9
9
  lib/visage/config/profiles.yaml
10
10
  _site
11
+ features/data/config/*/*.yaml
data/README.md CHANGED
@@ -129,6 +129,7 @@ Run all cucumber features:
129
129
  TODO
130
130
  ----
131
131
 
132
+ * make other lines slightly opaque when hovering over labels
132
133
  * detailed point-in-time data on hover (timestamp, value)
133
134
  * give graph profile an alternate private url
134
135
  * make notes/annotations on private url
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.7
1
+ 0.3.0
@@ -0,0 +1,16 @@
1
+ Feature: Build profiles
2
+ To find out how particular systems are performing
3
+ A user
4
+ Must be able to select a host
5
+ And some metrics
6
+ And see a page of graphs
7
+
8
+ Scenario: Build a simple profile
9
+ When I go to /builder
10
+ And I fill in "hosts" with "*"
11
+ And I fill in "metrics" with "*"
12
+ And I press "metrics"
13
+ And I fill in "profile_name" with "all on all"
14
+ And I press "create"
15
+ Then I should see a list of graphs
16
+ And I should see a profile heading
data/features/cli.feature CHANGED
@@ -14,4 +14,11 @@ Feature: command line utility
14
14
  When I start the visage server helper with "visage start"
15
15
  Then I should see "Looking for RRDs in /.*collectd" on the terminal
16
16
 
17
+ Scenario: Specified configuration directory
18
+ Given the "visage" gem is installed
19
+ And there is no file at "features/data/config/with_no_profiles/profiles.yaml"
20
+ When I start the visage server helper with "visage start" and the following variables:
21
+ | CONFIG_PATH |
22
+ | features/data/config/with_no_profiles |
23
+ Then I should see a file at "features/data/config/with_no_profiles/profiles.yaml"
17
24
 
File without changes
@@ -3,11 +3,6 @@ Feature: Visit site
3
3
  A user
4
4
  Must be able to visualise the data
5
5
 
6
- Scenario: Show graphs
7
- When I go to /profiles
8
- And I visit the first profile
9
- Then I should see a list of graphs
10
-
11
6
  Scenario: List profiles
12
7
  When I go to /profiles
13
8
  Then I should see a list of profiles
@@ -16,3 +11,17 @@ Feature: Visit site
16
11
  When I follow "name"
17
12
  Then I should see a list of profiles sorted alphabetically
18
13
 
14
+ Scenario: Show profile
15
+ When I go to /profiles
16
+ And I visit the first profile
17
+ Then I should see a list of graphs
18
+ And I should see a profile heading
19
+
20
+ Scenario: Navigate profiles
21
+ When I go to /profiles
22
+ And I visit the first profile
23
+ Then I should see a list of graphs
24
+ And I should see a profile heading
25
+ When I follow "back to profiles"
26
+ Then I should see a list of profiles
27
+
@@ -7,7 +7,7 @@ end
7
7
 
8
8
  Then /^I should see a list of graphs$/ do
9
9
  doc = Nokogiri::HTML(response_body)
10
- doc.search('div#profile div.graph').size.should > 1
10
+ doc.search('div#profile div.graph').size.should > 0
11
11
  end
12
12
 
13
13
  Then /^I should see a list of profiles$/ do
@@ -25,3 +25,12 @@ Then /^I should see a list of profiles sorted alphabetically$/ do
25
25
 
26
26
  unsorted.should == sorted
27
27
  end
28
+
29
+ Then /^I should see a profile heading$/ do
30
+ doc = Nokogiri::HTML(response_body)
31
+ doc.search('div#profile h2#profile_name').size.should == 1
32
+ end
33
+
34
+ Then /^show me the page source$/ do
35
+ puts response_body
36
+ end
@@ -23,3 +23,24 @@ Then /^I should see "([^"]*)" on the terminal$/ do |string|
23
23
  output = @pipe.read(250)
24
24
  output.should =~ /#{string}/
25
25
  end
26
+
27
+ Given /^there is no file at "([^"]*)"$/ do |filename|
28
+ FileUtils.rm_f(filename).should be_true
29
+ end
30
+
31
+ When /^I start the visage server helper with "([^"]*)" and the following variables:$/ do |cmd, table|
32
+ table.hashes.each do |hash|
33
+ hash.each_pair do |variable, value|
34
+ ENV[variable] = value
35
+ end
36
+ end
37
+ When %(I start the visage server helper with "#{cmd}")
38
+ end
39
+
40
+ Then /^I should see a file at "([^"]*)"$/ do |filename|
41
+ File.exists?(filename).should be_true
42
+ end
43
+
44
+ Then /^show me the output$/ do
45
+ puts @pipe.read(250)
46
+ end
@@ -10,6 +10,8 @@ require 'spec/expectations'
10
10
  require 'rack/test'
11
11
  require 'webrat'
12
12
 
13
+ ENV['CONFIG_PATH'] = @root.join('features/data/config/default')
14
+
13
15
  require app_file
14
16
  # Force the application name because polyglot breaks the auto-detection logic.
15
17
  Sinatra::Application.app_file = app_file
@@ -1,38 +1,57 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  @root = Pathname.new(File.dirname(__FILE__)).parent.parent.parent.expand_path
4
- @config_directory = Pathname.new(File.dirname(__FILE__)).expand_path
5
4
  require @root.join('lib/visage/config')
6
5
  require 'yaml'
7
6
 
8
- Visage::Config.use do |c|
9
- # setup profiles file
10
- profile_filename = @config_directory.join('profiles.yaml')
11
- unless File.exists?(profile_filename)
12
- FileUtils.touch(profile_filename)
13
- end
14
-
15
- # setup plugin colors file
16
- plugin_colors_filename = @config_directory.join('plugin-colors.yaml')
17
- unless File.exists?(plugin_colors_filename)
18
- puts "It's highly recommended you specify graph line colors in config/plugin-colors.yaml!"
19
- end
20
-
21
- # load config from profiles + plugin colors file
22
- [profile_filename, plugin_colors_filename].each do |filename|
23
- if File.exists?(filename)
24
- config = YAML::load_file(filename) || {}
25
- config.each_pair {|key, value| c[key] = value}
7
+ module Visage
8
+ class Config
9
+ class File
10
+ @@config_directories = []
11
+ @@config_directories << Pathname.new(::File.dirname(__FILE__)).expand_path
12
+ @@config_directories << Pathname.new(ENV["CONFIG_PATH"]).expand_path if ENV["CONFIG_PATH"]
13
+ @@config_directories.reverse!
14
+
15
+ def self.find(filename, opts={})
16
+ range = opts[:ignore_bundled] ? (0..-2) : (0..-1)
17
+ potential_filenames = @@config_directories[range].map {|d| d.join(filename)}
18
+ potential_filenames.find { |f| ::File.exists?(f) }
19
+ end
20
+
21
+ def self.load(filename, opts={})
22
+ unless path = self.find(filename, opts)
23
+ if opts[:create]
24
+ path = @@config_directories.first.join(filename)
25
+ begin
26
+ FileUtils.touch(path)
27
+ rescue Errno::EACCES => e
28
+ raise Errno::EACCES, "Couldn't write #{path}. Do you have CONFIG_PATH set?"
29
+ end
30
+ end
31
+ end
32
+
33
+ YAML::load_file(path)
34
+ end
35
+
36
+ def self.open(filename, &block)
37
+ path = self.find(filename)
38
+ ::File.open(path, 'r+') do |f|
39
+ block.call(f)
40
+ end
41
+ end
42
+
43
+ def initialize(filename, opts={})
44
+ unless ::File.exists?(filename)
45
+ path = @@config_directories.first.join(filename)
46
+ FileUtils.touch(path)
47
+ end
48
+ @file = ::File.open(filename, 'r+')
49
+ end
50
+
51
+ def to_s
52
+ @file.path
53
+ end
26
54
  end
27
55
  end
28
-
29
- # load fallback colors
30
- c['fallback_colors'] = YAML::load(File.read(@config_directory.join('fallback-colors.yaml')))
31
-
32
- # Location of collectd's RRD - you may want to edit this!
33
- c['rrddir'] = "/var/lib/collectd/rrd"
34
-
35
- # whether to shade in graphs
36
- c['shade'] = false
37
56
  end
38
57
 
@@ -1,67 +1,60 @@
1
1
  ---
2
- plugin_colors:
3
- memory:
4
- memory-used:
5
- value: "#cc0000"
6
- memory-buffered:
7
- value: "#edd400"
8
- memory-cached:
9
- value: "#3465a4"
10
- memory-free:
11
- value: "#73d216"
12
- cpu:
13
- cpu-idle:
14
- value: "#eeeeec"
15
- cpu-wait:
16
- value: "#edd400"
17
- cpu-user:
18
- value: "#3465a4"
19
- cpu-system:
20
- value: "#cc0000"
21
- cpu-nice:
22
- value: "#73d216"
23
- cpu-softirq:
24
- value: "#ad7fa8"
25
- cpu-interrupt:
26
- value: "#5c3566"
27
- cpu-steal:
28
- value: "#2e3436"
29
- swap:
30
- swap-used:
31
- value: "#cc0000"
32
- swap-free:
33
- value: "#73d216"
34
- swap-cached:
35
- value: "#3465a4"
36
- swap_io-in:
37
- value: "#edd400"
38
- swap_io-out:
39
- value: "#5c3566"
2
+ memory:
3
+ memory-used:
4
+ value: "#cc0000"
5
+ memory-buffered:
6
+ value: "#edd400"
7
+ memory-cached:
8
+ value: "#3465a4"
9
+ memory-free:
10
+ value: "#73d216"
11
+ cpu:
12
+ cpu-idle:
13
+ value: "#eeeeec"
14
+ cpu-wait:
15
+ value: "#edd400"
16
+ cpu-user:
17
+ value: "#3465a4"
18
+ cpu-system:
19
+ value: "#cc0000"
20
+ cpu-nice:
21
+ value: "#73d216"
22
+ cpu-softirq:
23
+ value: "#ad7fa8"
24
+ cpu-interrupt:
25
+ value: "#5c3566"
26
+ cpu-steal:
27
+ value: "#2e3436"
28
+ swap:
29
+ swap-used:
30
+ value: "#cc0000"
31
+ swap-free:
32
+ value: "#73d216"
33
+ swap-cached:
34
+ value: "#3465a4"
35
+ swap_io-in:
36
+ value: "#edd400"
37
+ swap_io-out:
38
+ value: "#5c3566"
39
+ load:
40
40
  load:
41
- load:
42
- longterm: "#ef2929"
43
- shortterm: "#73d216"
44
- midterm: "#3465a4"
41
+ longterm: "#ef2929"
42
+ shortterm: "#73d216"
43
+ midterm: "#3465a4"
44
+ df:
45
45
  df:
46
- df:
47
- used: "#cc0000"
48
- free: "#73d216"
49
- disk:
50
- disk_merged:
51
- read: "#8ae234"
52
- write: "#ef2929"
53
- disk_time:
54
- read: "#729fcf"
55
- write: "#fcaf3e"
56
- disk_ops:
57
- read: "#ad7fa8"
58
- write: "#fce94f"
59
- disk_octets:
60
- read: "#c17d11"
61
- write: "#888a85"
62
-
63
-
64
-
65
-
66
-
67
-
46
+ used: "#cc0000"
47
+ free: "#73d216"
48
+ disk:
49
+ disk_merged:
50
+ read: "#8ae234"
51
+ write: "#ef2929"
52
+ disk_time:
53
+ read: "#729fcf"
54
+ write: "#fcaf3e"
55
+ disk_ops:
56
+ read: "#ad7fa8"
57
+ write: "#fce94f"
58
+ disk_octets:
59
+ read: "#c17d11"
60
+ write: "#888a85"
data/lib/visage/config.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  module Visage
2
2
  class Config
3
-
4
3
  class << self
5
4
  def use
6
5
  @configuration ||= {}
@@ -11,18 +11,15 @@ module Visage
11
11
  attr_reader :options, :selected_hosts, :hosts, :selected_metrics, :metrics,
12
12
  :name, :errors
13
13
 
14
- @@root = Pathname.new(File.dirname(__FILE__)).parent.parent.expand_path
15
- @@profiles_filename = @@root.join('lib/visage/config/profiles.yaml')
16
-
17
14
  def self.get(id)
18
15
  url = id.downcase.gsub(/[^\w]+/, "+")
19
- profiles = YAML::load_file(@@profiles_filename) || {}
16
+ profiles = Visage::Config.profiles || {}
20
17
  profiles[url] ? self.new(profiles[url]) : nil
21
18
  end
22
19
 
23
20
  def self.all(opts={})
24
21
  sort = opts[:sort]
25
- profiles = YAML::load_file(@@profiles_filename) || {}
22
+ profiles = Visage::Config.profiles || {}
26
23
  profiles = sort == "name" ? profiles.sort.map {|i| i.last } : profiles.values
27
24
  profiles.map { |prof| self.new(prof) }
28
25
  end
@@ -66,10 +63,10 @@ module Visage
66
63
  :url => @options[:profile_name].downcase.gsub(/[^\w]+/, "+") }
67
64
 
68
65
  # Save it.
69
- profiles = YAML::load_file(@@profiles_filename) || {}
66
+ profiles = Visage::Config.profiles || {}
70
67
  profiles[attrs[:url]] = attrs
71
68
 
72
- File.open(@@profiles_filename, 'w') do |file|
69
+ Visage::Config::File.open('profiles.yaml') do |file|
73
70
  file << profiles.to_yaml
74
71
  end
75
72
 
@@ -76,7 +76,10 @@ var visageBase = new Class({
76
76
  header = $chk(this.options.name) ? this.options.name : this.options.plugin
77
77
  this.graphHeader = new Element('h3', {
78
78
  'class': 'graph-title',
79
- 'html': header
79
+ 'html': header,
80
+ 'styles': {
81
+ 'color': "#121212"
82
+ }
80
83
  });
81
84
  $(this.parentElement).grab(this.graphHeader);
82
85
  },
@@ -15,10 +15,9 @@ a {
15
15
  /* styles */
16
16
 
17
17
  a { color: #3465a4; padding: 2px 0px; }
18
- a:hover { color: white; background-color: #3465a4; }
18
+ a:hover { text-decoration: underline; }
19
19
  a:visited { color: #75507b; }
20
- a:hover:visited { color: white; background-color: #75507b; }
21
- a:active { color: white; background-color: #cc0000; }
20
+ a:hover:visited { text-decoration: underline; }
22
21
 
23
22
  p {
24
23
  margin: 0 0 1em;
@@ -34,6 +33,8 @@ div#header {
34
33
  background-color: #3465a4;
35
34
  margin-bottom: 16px;
36
35
  border-bottom: 6px solid #c5d6ed;
36
+ background: -moz-linear-gradient(100% 100% 90deg, #3465a4, #142e52);
37
+ background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#142e52), to(#3465a4));
37
38
  }
38
39
 
39
40
  div#nav {
@@ -53,7 +54,7 @@ div#nav a {
53
54
 
54
55
  div#nav a:hover {
55
56
  text-shadow: 0 1px 6px rgba(115,157,211,0.9);
56
- background-color: #3465a4;
57
+ text-decoration: none;
57
58
  }
58
59
 
59
60
  div#content, div#nav {
@@ -206,7 +207,7 @@ h1, h2, h3, h4, h5 {
206
207
 
207
208
  h2 {
208
209
  font-weight: bold;
209
- color: #666;
210
+ color: #333;
210
211
  border-bottom: 4px solid #888;
211
212
  margin-bottom: 12px;
212
213
  }
@@ -221,6 +222,27 @@ h1 span.project-name {
221
222
  color: white;
222
223
  }
223
224
 
225
+ h2#profile_name {
226
+ margin: 36px 0;
227
+ }
228
+
229
+ h4.error {
230
+ margin: 8px 0;
231
+ }
232
+
233
+ div.graph pre {
234
+ line-height: 1.6em;
235
+ font-size: 90%;
236
+ margin-top: -10px;
237
+ }
238
+
239
+ div#bottom_nav {
240
+ margin: 32px 0;
241
+ border-top: 1px dotted #aaa;
242
+ padding-top: 12px;
243
+ font-size: 85%;
244
+ }
245
+
224
246
  div#footer {
225
247
  font-size: 0.6em;
226
248
  color: gray;
@@ -255,5 +277,8 @@ div#profiles ul {
255
277
  }
256
278
 
257
279
  div#profiles p.create {
280
+ border-top: 1px dotted #aaa;
281
+ padding-top: 8px;
282
+ margin-top: 24px;
258
283
  font-size: 85%;
259
284
  }
@@ -10,7 +10,6 @@
10
10
  %script{:type => "text/javascript", :src => link_to("/javascripts/mootools-1.2.3-core.js")}
11
11
  %script{:type => "text/javascript", :src => link_to("/javascripts/mootools-1.2.3.1-more.js")}
12
12
  %script{:type => "text/javascript", :src => link_to("/javascripts/graph.js")}
13
- %script{:type => "text/javascript", :src => link_to("/javascripts/application.js")}
14
13
 
15
14
  %body
16
15
  %div#wrapper
@@ -1,6 +1,8 @@
1
- - page_title @profile.options[:profile_name]
1
+ - profile_name = @profile.options[:profile_name]
2
+ - page_title(profile_name)
2
3
 
3
4
  %div#profile
5
+ %h2#profile_name= profile_name
4
6
  - @profile.graphs.each do |graph|
5
7
  %div{:id => graph.id, :class => 'graph'}
6
8
  :javascript
@@ -17,4 +19,17 @@
17
19
  finish: '#{@finish}'
18
20
  });
19
21
  });
22
+ - if @profile.graphs.size == 0
23
+ %div.graph
24
+ %h4.error Oops! Looks like there aren't any graphs matching the specified patterns.
25
+ %p These are the patterns:
26
+ %pre
27
+ :preserve
28
+ Host: #{@profile.options[:hosts]}
29
+ Metrics: #{@profile.options[:metrics]}
30
+
31
+ %div#bottom_nav
32
+ %a{:href => link_to("/profiles")} &larr; Back to profiles
33
+
34
+
20
35
 
@@ -14,5 +14,5 @@
14
14
  %a{:href => link_to("/profiles/#{prof.url}")}= prof.profile_name
15
15
 
16
16
  %p.create
17
- %a{:href => link_to("/builder")} Create a new profile.
17
+ %a{:href => link_to("/builder")} Create a profile
18
18
 
data/lib/visage-app.rb CHANGED
@@ -22,6 +22,19 @@ module Visage
22
22
 
23
23
  helpers Sinatra::LinkToHelper
24
24
  helpers Sinatra::PageTitleHelper
25
+
26
+ configure do
27
+ Visage::Config.use do |c|
28
+ # Base configuration files.
29
+ c['profiles'] = Visage::Config::File.load('profiles.yaml', :create => true, :ignore_bundled => true)
30
+ c['plugin_colors'] = Visage::Config::File.load('plugin-colors.yaml')
31
+ c['fallback_colors'] = Visage::Config::File.load('fallback-colors.yaml')
32
+
33
+ # FIXME: make this configurable through file
34
+ c['shade'] = false
35
+ c['rrddir'] = ENV["RRDDIR"] ? Pathname.new(ENV["RRDDIR"]).expand_path : Pathname.new("/var/lib/collectd/rrd").expand_path
36
+ end
37
+ end
25
38
  end
26
39
 
27
40
  class Profiles < Application
@@ -32,7 +45,7 @@ module Visage
32
45
  get '/profiles/:url' do
33
46
  @profile = Visage::Profile.get(params[:url])
34
47
  raise Sinatra::NotFound unless @profile
35
- @start = params[:start]
48
+ @start = params[:start]
36
49
  @finish = params[:finish]
37
50
  haml :profile
38
51
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 2
8
- - 7
9
- version: 0.2.7
7
+ - 3
8
+ - 0
9
+ version: 0.3.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Lindsay Holmwood
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-09-26 00:00:00 +10:00
17
+ date: 2010-10-15 00:00:00 +11:00
18
18
  default_executable: visage
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -101,9 +101,11 @@ files:
101
101
  - Rakefile
102
102
  - VERSION
103
103
  - bin/visage
104
+ - features/builder.feature
104
105
  - features/cli.feature
106
+ - features/data/config/with_no_profiles/.stub
105
107
  - features/json.feature
106
- - features/site.feature
108
+ - features/profiles.feature
107
109
  - features/step_definitions/form_steps.rb
108
110
  - features/step_definitions/json_steps.rb
109
111
  - features/step_definitions/result_steps.rb
@@ -129,7 +131,6 @@ files:
129
131
  - lib/visage/public/images/hosts.png
130
132
  - lib/visage/public/images/metrics.png
131
133
  - lib/visage/public/images/search.png
132
- - lib/visage/public/javascripts/application.js
133
134
  - lib/visage/public/javascripts/g.line-min.js
134
135
  - lib/visage/public/javascripts/g.line.js
135
136
  - lib/visage/public/javascripts/g.raphael-min.js
@@ -1,4 +0,0 @@
1
- window.addEvent('domready', function () {
2
-
3
- });
4
-