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.
Files changed (42) hide show
  1. data/.gitignore +10 -0
  2. data/CHANGELOG.md +12 -0
  3. data/Gemfile +1 -15
  4. data/Gemfile.lock +44 -42
  5. data/README.md +123 -49
  6. data/Rakefile +16 -26
  7. data/bin/visage-app +17 -4
  8. data/features/cli.feature +10 -3
  9. data/features/json.feature +37 -0
  10. data/features/step_definitions/{visage_steps.rb → cli_steps.rb} +1 -1
  11. data/features/step_definitions/json_steps.rb +50 -8
  12. data/features/step_definitions/site_steps.rb +1 -1
  13. data/features/support/config/default/profiles.yaml +335 -0
  14. data/features/{data → support}/config/with_no_profiles/.stub +0 -0
  15. data/features/support/config/with_no_profiles/profiles.yaml +0 -0
  16. data/features/support/config/with_old_profile_yaml/profiles.yaml +116 -0
  17. data/features/support/env.rb +2 -3
  18. data/lib/visage-app.rb +35 -25
  19. data/lib/visage-app/collectd/json.rb +115 -118
  20. data/lib/visage-app/collectd/rrds.rb +25 -19
  21. data/lib/visage-app/helpers.rb +17 -0
  22. data/lib/visage-app/profile.rb +18 -25
  23. data/lib/visage-app/public/images/caution.png +0 -0
  24. data/lib/visage-app/public/images/ok.png +0 -0
  25. data/lib/visage-app/public/images/questions.png +0 -0
  26. data/lib/visage-app/public/javascripts/builder.js +607 -0
  27. data/lib/visage-app/public/javascripts/graph.js +179 -142
  28. data/lib/visage-app/public/javascripts/message.js +520 -0
  29. data/lib/visage-app/public/javascripts/mootools-core-1.4.0-full-compat.js +6285 -0
  30. data/lib/visage-app/public/javascripts/mootools-more-1.4.0.1.js +6399 -0
  31. data/lib/visage-app/public/stylesheets/message.css +61 -0
  32. data/lib/visage-app/public/stylesheets/screen.css +149 -38
  33. data/lib/visage-app/version.rb +5 -0
  34. data/lib/visage-app/views/builder.haml +38 -49
  35. data/lib/visage-app/views/builder_form.haml +14 -0
  36. data/lib/visage-app/views/layout.haml +5 -2
  37. data/lib/visage-app/views/profile.haml +44 -25
  38. data/visage-app.gemspec +29 -132
  39. metadata +93 -150
  40. data/VERSION +0 -1
  41. data/features/builder.feature +0 -16
  42. data/lib/visage-app/collectd/profile.rb +0 -36
@@ -0,0 +1,61 @@
1
+ @charset "utf-8";
2
+ /* CSS Document */
3
+
4
+ /* Message Boxes */
5
+ .msgBox{
6
+ font-family:Arial, Helvetica, sans-serif;
7
+ opacity: 0;
8
+ position:absolute;
9
+ top:-1000px;
10
+ left:0px;
11
+ max-width: 250px;
12
+ min-width: 150px;
13
+ color:#aaa;
14
+ background: rgb(0, 0, 0); /* compatibility fallback */
15
+ background: rgba(0, 0, 0, 0.8);
16
+ padding: 10px;
17
+ border-radius: 15px;
18
+ box-shadow: 2px 2px 6px #666;
19
+ -moz-border-radius: 15px;
20
+ -webkit-border-radius: 15px;
21
+ -moz-box-shadow: 2px 2px 6px #666;
22
+ -webkit-box-shadow: 2px 2px 5px #666;
23
+ z-index:-1;
24
+ }
25
+
26
+ .msgBoxImage{
27
+ width: 40px;
28
+ height: 40px;
29
+ }
30
+
31
+ .msgBoxIcon{
32
+ float:left;
33
+ width: 40px;
34
+ height: 40px;
35
+ padding-right: 7px;
36
+ }
37
+
38
+ .msgBoxTitle{
39
+ float:left;
40
+ color: #FFFFFF;
41
+ }
42
+
43
+ .msgBoxContent{
44
+ float:left;
45
+ max-width: 80%;
46
+ font-size:12px;
47
+ }
48
+
49
+ .msgBoxMessage{ float:left;}
50
+ .msgBoxLink{ color:#6699CC;}
51
+ .msgBoxLink:hover{ color:#FF9900;}
52
+
53
+ .msgEditable{
54
+ font-family:Arial, Helvetica, sans-serif;
55
+ font-size:12px;
56
+ width:250px;
57
+ background: rgb(0, 0, 0); /* compatibility fallback */
58
+ background: rgba(255, 255, 255, 0.1);
59
+ border:#000;
60
+ color:#FFF;
61
+ }
@@ -12,6 +12,10 @@ a {
12
12
  text-decoration: none;
13
13
  }
14
14
 
15
+ input:focus {
16
+ outline: none;
17
+ }
18
+
15
19
  /* styles */
16
20
 
17
21
  a { color: #3465a4; padding: 2px 0px; }
@@ -124,10 +128,9 @@ background-image: -moz-linear-gradient(
124
128
  );
125
129
  }
126
130
 
127
- p.error {
128
- color: red;
129
- font-size: 80%;
130
- }
131
+ /*
132
+ * BUILDER
133
+ */
131
134
 
132
135
  div.builder {
133
136
  float: left;
@@ -145,25 +148,49 @@ div.builder input.text {
145
148
  -webkit-box-shadow: 0 1px 5px rgba(0, 0, 0, 0.25);
146
149
  }
147
150
 
148
- div.builder input.text:focus {
149
- border: 1px solid #666;
151
+ div.builder div.tokenWrapper {
152
+ border: 1px solid #bbb;
153
+ font-size: 16px;
154
+ padding: 4px;
155
+ padding-top: 0;
156
+ width: 350px;
157
+ height: 20px;
158
+ -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
159
+ -webkit-box-shadow: 0 1px 5px rgba(0, 0, 0, 0.25);
150
160
  }
151
161
 
152
- div.builder input.submit {
153
- opacity: 0;
162
+ div.builder div.tokenWrapper div.token {
163
+ float: left;
164
+ margin: 2px 4px 2px 0px;
165
+ font-family: Lucida Grande, sans-serif;
166
+ font-size: 16px;
154
167
  }
155
168
 
156
- div.builder ul {
157
- margin-top: 16px;
169
+ div.builder div.tokenWrapper div.token.finalized {
170
+ padding: 0 2px;
171
+ background-color: #D1E5FF;
172
+ color: black;
173
+ border: 1px solid #2F5A92;
174
+ font-size: 14px;
158
175
  }
159
176
 
160
- div.builder img.icon {
161
- float: right;
177
+ div.builder div.tokenWrapper div.token.finalized.selected {
178
+ background-color: #2F5A92;
179
+ color: white;
162
180
  }
163
181
 
164
- div.builder li {
165
- list-style-type: none;
166
- padding: 4px 8px;
182
+ div.builder div.tokenWrapper div.token input.token {
183
+ border: 0;
184
+ font-size: 16px;
185
+ width: 80px;
186
+ }
187
+
188
+ div.builder input.text:focus {
189
+ border: 1px solid #666;
190
+ }
191
+
192
+ div.builder img.icon {
193
+ float: right;
167
194
  }
168
195
 
169
196
  div.builder h5 {
@@ -171,43 +198,48 @@ div.builder h5 {
171
198
  margin-left: 8px;
172
199
  }
173
200
 
174
- div.builder ul.available {
175
- margin-top: 4px;
201
+ div.builder ul.results {
202
+ font-family: Lucida Grande, sans-serif;
203
+ font-size: 16px;
204
+ position: absolute;
205
+ z-index: 10;
176
206
  }
177
207
 
178
- div.builder ul.selected {
179
- background-color: #fffaba;
208
+ div.builder ul.results li {
209
+ list-style-type: none;
210
+ padding: 4px 5px;
211
+ border-left: 1px #BBBBBB solid;
212
+ border-right: 1px #BBBBBB solid;
213
+ border-bottom: 1px #BBBBBB solid;
214
+ background-color: white;
215
+ width: 348px;
180
216
  }
181
217
 
182
- div.builder li:hover {
183
- /*background-color: #fffaba;*/
218
+ div.builder ul.results li.active {
219
+ background-color: #4C94F0;
220
+ color: white;
184
221
  }
185
222
 
186
- div.builder li td {
187
- padding: 4px;
223
+ div.builder ul.results li.all {
224
+ color: gray;
188
225
  }
189
226
 
190
- div.builder li td.name {
191
- width: 350px;
227
+ div.builder ul.results li.all.active {
228
+ background-color: #A0BD29;
229
+ color: white;
192
230
  }
193
231
 
194
- div.builder li td.meta {
195
- width: 80px;
196
- text-align: right;
197
- padding-right: 8px;
198
- }
199
232
 
200
- span.glob.example {
201
- display: block;
202
- font-size: 80%;
203
- font-style: italic;
204
- margin-top: 8px;
205
- }
233
+
234
+ /*
235
+ * GRAPHS + PROFILE VIEWER
236
+ */
206
237
 
207
238
  div#graphs {
239
+ clear: both;
208
240
  padding: 0 2em;
209
- border-top: 1px dashed #babdb6;
210
- margin-top: 2em;
241
+ /* border-top: 1px dashed #babdb6; */
242
+ margin: 16px 0;
211
243
  }
212
244
 
213
245
  div.graph {
@@ -235,6 +267,21 @@ h1 span.project-name {
235
267
  color: white;
236
268
  }
237
269
 
270
+ a#edit {
271
+ float: right;
272
+ display: block;
273
+ }
274
+
275
+ div#chart-builder-slider {
276
+ clear: both;
277
+ overflow-x: hidden;
278
+ overflow-y: hidden;
279
+ }
280
+
281
+ div#profile {
282
+ clear: both;
283
+ }
284
+
238
285
  h2#profile_name {
239
286
  margin: 36px 0;
240
287
  }
@@ -331,3 +378,67 @@ div#shortcuts table td, div#shortcuts table th {
331
378
  div#shortcuts table th {
332
379
  text-align: left;
333
380
  }
381
+
382
+
383
+ .button {
384
+ -moz-border-radius: 5px;
385
+ -webkit-border-radius: 5px;
386
+ -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
387
+ -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
388
+ background-color: #637020;
389
+ background-repeat: repeat-x;
390
+ background: -moz-linear-gradient(100% 100% 90deg, #244671, #4C94F0);
391
+ background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#4C94F0), to(#244671));
392
+ border: medium none;
393
+ color: white;
394
+ cursor: pointer;
395
+ font-family: Lucida Grande,sans-serif;
396
+ font-weight: bold;
397
+ padding: 4px;
398
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.75);
399
+ }
400
+
401
+ .button:hover, .button:focus {
402
+ text-shadow: 0 2px 1px rgba(255,255,255,0.1);
403
+ background: -webkit-gradient(linear, left top, left bottom, from(#529EFF), to(#274C7A));
404
+ background: -moz-linear-gradient(top, #529EFF, #274C7A);
405
+ outline: none;
406
+ }
407
+
408
+ input#show {
409
+ float: right;
410
+ font-size: 80%;
411
+ padding: 4px 8px;
412
+ font-family: 'Bitstream Vera Sans', 'Helvetica Neue', sans-serif;
413
+ margin-bottom: 16px;
414
+ margin-left: 16px;
415
+ }
416
+
417
+ input#save {
418
+ float: right;
419
+ font-size: 80%;
420
+ padding: 4px 8px;
421
+ font-family: 'Bitstream Vera Sans', 'Helvetica Neue', sans-serif;
422
+ margin-bottom: 16px;
423
+ margin-left: 16px;
424
+ }
425
+
426
+ button::-moz-focus-inner,
427
+ input[type="reset"]::-moz-focus-inner,
428
+ input[type="button"]::-moz-focus-inner,
429
+ input[type="submit"]::-moz-focus-inner,
430
+ input[type="file"] > input[type="button"]::-moz-focus-inner {
431
+ border: none;
432
+ }
433
+
434
+ input#profile_name {
435
+ border: 1px solid #bbb;
436
+ font-size: 16px;
437
+ padding: 4px;
438
+ padding-top: 0;
439
+ width: 200px;
440
+ height: 20px;
441
+ -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
442
+ -webkit-box-shadow: 0 1px 5px rgba(0, 0, 0, 0.25);
443
+ float: right;
444
+ }
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module Visage
4
+ VERSION = "2.0.0"
5
+ end
@@ -1,52 +1,41 @@
1
1
  - page_title "Profile builder"
2
-
3
- %div#builder
4
- %form{:action => "/builder", :method => :get}
5
-
6
- - if @profile.selected_hosts.size > 0 && @profile.selected_metrics.size > 0
7
- %div#profile-preview
8
- %h2 Preview
9
- %div#graph
10
- %p Coming soon!
11
- %div#profile-meta
12
- %h2 Profile name
13
- %p
14
- %input{:name => "profile_name", :value => params[:profile_name], :type => "text", :class => "text"}
15
- %p.error
16
- = @profile.errors[:profile_name]
17
- %p
18
- %input{:type => "submit", :value => "create", :class => "create", :name => "submit"}
19
-
20
- %div#hosts.builder
21
- %h2 Hosts
22
- %input{:name => "hosts", :type => "text", :class => "text", :value => params[:hosts]}
23
- %input{:name => "submit", :type => "submit", :class => "submit", :value => "hosts"}
24
- %img{:src => link_to("/images/hosts.png"), :class => "icon hosts"}
25
- %span{:class => "glob example"} e.g. “charlie”, “*.bravo*”, “echo, foxtrot*”
26
-
27
- %ul.selected
28
- - @profile.selected_hosts.each do |name|
29
- %li= name
30
-
31
- %h5 Available
32
- %ul.available
33
- - @profile.hosts.each do |name|
34
- %li= name
35
-
36
- %div#metrics.builder
37
- %h2 Metrics
38
- %input{:name => "metrics", :type => "text", :class => "text", :value => params[:metrics]}
39
- %input{:name => "submit", :type => "submit", :class => "submit", :value => "metrics"}
40
- %img{:src => link_to("/images/metrics.png"), :class => "icon metrics"}
41
- %span{:class => "glob example"} e.g. “cpu*/*”, “disk*/*ops, disk*/*time”
42
-
43
- %ul.selected
44
- - @profile.selected_metrics.each do |name|
45
- %li= name
46
-
47
- %h5 Available
48
- %ul.available
49
- - @profile.metrics.each do |name|
50
- %li= name
2
+ - require_js "builder"
3
+ :javascript
4
+ window.addEvent('domready', function() {
5
+ new ChartBuilder('chart-builder');
6
+ });
7
+
8
+ %div#chart-builder
9
+ %div#hosts.builder
10
+ %h2 Hosts
11
+ %img{:src => link_to("/images/hosts.png"), :class => "icon hosts"}
12
+ %div.search
13
+
14
+ %div#metrics.builder
15
+ %h2 Metrics
16
+ %img{:src => link_to("/images/metrics.png"), :class => "icon metrics"}
17
+ %div.search
18
+
19
+ %input#show{:value => "Show graphs", :type => "button", :class => "button"}
20
+
21
+ %div{:style => "text-align: left; margin-left: 24px;"}
22
+ %strong{:style => "font-family: sans-serif; font-size: 12px;"}
23
+ Global Time Period:
24
+ <select id="timescale" class="date timescale" style="margin-bottom: 3px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: rgb(170, 170, 170); border-right-color: rgb(170, 170, 170); border-bottom-color: rgb(170, 170, 170); border-left-color: rgb(170, 170, 170); "><option value="start=1321692809">last 1 hour</option><option value="start=1321689209">last 2 hours</option><option value="start=1321674809">last 6 hours</option><option value="start=1321653209">last 12 hours</option><option value="start=1321610009">last 1 day</option><option value="start=1321523609">last 2 days</option><option value="start=1321437209">last 3 days</option><option value="start=1321091609">last 1 week</option><option value="start=1320486809">last 2 weeks</option><option value="start=1318910009">last 1 month</option><option value="start=1290160409">last 1 year</option><option value="start=1258624409">last 2 years</option></select>
25
+ :javascript
26
+ window.addEvent('domready', function() {
27
+ var timescale = $('timescale');
28
+ timescale.addEvent('change', function(e) {
29
+ e.stop();
30
+ window.Graphs.each(function(graph) {
31
+ var selected = timescale.getSelected()[0]
32
+
33
+ graph.setTimePeriodTo(selected);
34
+ });
35
+
36
+ });
37
+ });
38
+
39
+ %div#graphs
51
40
 
52
41
 
@@ -0,0 +1,14 @@
1
+ %div#chart-builder
2
+ %div#hosts.builder
3
+ %h2 Hosts
4
+ %img{:src => link_to("/images/hosts.png"), :class => "icon hosts"}
5
+ %div.search
6
+
7
+ %div#metrics.builder
8
+ %h2 Metrics
9
+ %img{:src => link_to("/images/metrics.png"), :class => "icon metrics"}
10
+ %div.search
11
+
12
+ %input#show{:value => "Show graphs", :type => "button", :class => "button"}
13
+
14
+ %div#graph
@@ -4,11 +4,14 @@
4
4
  %title= include_page_title
5
5
  %link{:rel => "icon", :type => "image/gif", :href => "/favicon.gif"}
6
6
  %link{:rel => 'stylesheet', :href => '/stylesheets/screen.css', :type => 'text/css'}
7
- %script{:type => "text/javascript", :src => link_to("/javascripts/mootools-1.2.3-core.js")}
8
- %script{:type => "text/javascript", :src => link_to("/javascripts/mootools-1.2.5.1-more.js")}
7
+ %link{:rel => 'stylesheet', :href => '/stylesheets/message.css', :type => 'text/css'}
8
+ %script{:type => "text/javascript", :src => link_to("/javascripts/mootools-core-1.4.0-full-compat.js")}
9
+ %script{:type => "text/javascript", :src => link_to("/javascripts/mootools-more-1.4.0.1.js")}
9
10
  %script{:type => "text/javascript", :src => link_to("/javascripts/highcharts.js")}
10
11
  %script{:type => "text/javascript", :src => link_to("/javascripts/graph.js")}
11
12
  %script{:type => "text/javascript", :src => link_to("/javascripts/keyboard.js")}
13
+ %script{:type => "text/javascript", :src => link_to("/javascripts/message.js")}
14
+ = include_required_js
12
15
 
13
16
  %body
14
17
  %div#wrapper
@@ -1,33 +1,52 @@
1
1
  - profile_name = @profile.options[:profile_name]
2
2
  - page_title(profile_name)
3
+ - require_js "builder"
4
+
5
+ %a{:href=>"#edit", :id => "edit"} edit profile
6
+ %div#chart-builder-slider
7
+ = haml :builder_form
8
+
9
+ :javascript
10
+ window.addEvent('domready', function() {
11
+ var builder = new ChartBuilder('chart-builder', {
12
+ hosts: ['#{@profile.options[:hosts].join("','") }'],
13
+ metrics: ['#{@profile.options[:metrics].join("','") }']
14
+ });
15
+
16
+ $('chart-builder-slider').fade('hide').setStyle('height', '0');
17
+
18
+ $('edit').addEvent('click', function(e) {
19
+ e.stop();
20
+ $('chart-builder-slider').setStyle('height', 'auto').fade('in');
21
+ e.target.destroy();
22
+ });
23
+ });
3
24
 
4
25
  %div#profile
5
- %h2#profile_name= profile_name
6
- - @profile.graphs.each do |graph|
7
- - id = URI.escape(graph.id)
8
- - host = URI.escape(graph.host)
9
- - plugin = URI.escape(graph.plugin)
10
- - instance = URI.escape(graph.instances.join(','))
11
- %div{:id => id, :class => 'graph'}
12
- %img{:src => link_to("/images/loader.gif")}
13
- :javascript
14
- window.addEvent('domready', function() {
15
- var graph = new visageGraph('#{id}', '#{host}', '#{plugin}', {
16
- pluginInstance: '#{instance}',
17
- start: '#{@start}',
18
- finish: '#{@finish}',
19
- live: #{@live},
20
- #{ "baseurl: '" + ENV['BASE_URL'].gsub(/^\//, '') if ENV['BASE_URL'] }
26
+ %h2#name= profile_name
27
+ %div#graphs
28
+ - @profile.graphs.each do |graph|
29
+ - id = URI.escape(graph.id)
30
+ - host = URI.escape(graph.host)
31
+ - plugin = URI.escape(graph.plugin)
32
+ - instance = URI.escape(graph.instances.join(','))
33
+ %div{:id => id, :class => 'graph'}
34
+ %img{:src => link_to("/images/loader.gif")}
35
+ :javascript
36
+ window.addEvent('domready', function() {
37
+ var graph = new VisageGraph('#{id}', '#{host}', '#{plugin}', {
38
+ pluginInstance: '#{instance}',
39
+ #{ "baseurl: '" + ENV['BASE_URL'].gsub(/^\//, '') if ENV['BASE_URL'] }
40
+ });
21
41
  });
22
- });
23
- - if @profile.graphs.size == 0
24
- %div.graph
25
- %h4.error Oops! Looks like there aren't any graphs matching the specified patterns.
26
- %p These are the patterns:
27
- %pre
28
- :preserve
29
- Host: #{@profile.options[:hosts]}
30
- Metrics: #{@profile.options[:metrics]}
42
+ - if @profile.graphs.size == 0
43
+ %div.graph
44
+ %h4.error Oops! Looks like there aren't any graphs matching the specified patterns.
45
+ %p These are the patterns:
46
+ %pre
47
+ :preserve
48
+ Host: #{@profile.options[:hosts]}
49
+ Metrics: #{@profile.options[:metrics]}
31
50
 
32
51
  %div#bottom_nav
33
52
  %a{:href => link_to("/profiles")} &larr; Back to profiles