ganglia_js_charts 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,22 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ bin
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 James Golick
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,17 @@
1
+ = ganglia_js_charts
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
13
+ * Send me a pull request. Bonus points for topic branches.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2011 James Golick. See LICENSE for details.
@@ -0,0 +1,45 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "ganglia_js_charts"
8
+ gem.summary = %Q{}
9
+ gem.description = %Q{}
10
+ gem.email = "jamesgolick@gmail.com"
11
+ gem.homepage = "http://github.com/jamesgolick/ganglia_js_charts"
12
+ gem.authors = ["James Golick"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ require 'spec/rake/spectask'
22
+ Spec::Rake::SpecTask.new(:spec) do |spec|
23
+ spec.libs << 'lib' << 'spec'
24
+ spec.spec_files = FileList['spec/**/*_spec.rb']
25
+ end
26
+
27
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.pattern = 'spec/**/*_spec.rb'
30
+ spec.rcov = true
31
+ end
32
+
33
+ task :spec => :check_dependencies
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
+
41
+ rdoc.rdoc_dir = 'rdoc'
42
+ rdoc.title = "ganglia_js_charts #{version}"
43
+ rdoc.rdoc_files.include('README*')
44
+ rdoc.rdoc_files.include('lib/**/*.rb')
45
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,80 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{ganglia_js_charts}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["James Golick"]
12
+ s.date = %q{2011-01-23}
13
+ s.default_executable = %q{ganglia_js_charts_server}
14
+ s.description = %q{}
15
+ s.email = %q{jamesgolick@gmail.com}
16
+ s.executables = ["ganglia_js_charts_server"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.rdoc"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ ".gitignore",
24
+ "LICENSE",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "ganglia_js_charts.gemspec",
29
+ "lib/ganglia_js_charts.rb",
30
+ "public/ajax-loader.gif",
31
+ "public/ganglia-js.js",
32
+ "public/index.html",
33
+ "public/jquery-1.4.4.js",
34
+ "public/jquery.tmpl.js",
35
+ "public/js/adapters/mootools-adapter.js",
36
+ "public/js/adapters/mootools-adapter.src.js",
37
+ "public/js/adapters/prototype-adapter.js",
38
+ "public/js/adapters/prototype-adapter.src.js",
39
+ "public/js/highcharts.js",
40
+ "public/js/highcharts.src.js",
41
+ "public/js/modules/exporting.js",
42
+ "public/js/modules/exporting.src.js",
43
+ "public/js/themes/dark-blue.js",
44
+ "public/js/themes/dark-green.js",
45
+ "public/js/themes/gray.js",
46
+ "public/js/themes/grid.js",
47
+ "public/json2.js",
48
+ "public/knockout-1.1.2.debug.js",
49
+ "public/underscore.js",
50
+ "spec/ganglia_js_charts_spec.rb",
51
+ "spec/spec.opts",
52
+ "spec/spec_helper.rb",
53
+ "test-rrds/FetLife/feed01.dal.fetlife/like_service_LikeService_create-count.rrd",
54
+ "test-rrds/FetLife/feed02.dal.fetlife/like_service_LikeService_create-count.rrd",
55
+ "test-rrds/graphs.json"
56
+ ]
57
+ s.homepage = %q{http://github.com/jamesgolick/ganglia_js_charts}
58
+ s.rdoc_options = ["--charset=UTF-8"]
59
+ s.require_paths = ["lib"]
60
+ s.rubygems_version = %q{1.3.7}
61
+ s.summary = %q{}
62
+ s.test_files = [
63
+ "spec/ganglia_js_charts_spec.rb",
64
+ "spec/spec_helper.rb"
65
+ ]
66
+
67
+ if s.respond_to? :specification_version then
68
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
69
+ s.specification_version = 3
70
+
71
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
72
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
73
+ else
74
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
75
+ end
76
+ else
77
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
78
+ end
79
+ end
80
+
@@ -0,0 +1,61 @@
1
+ require "rubygems"
2
+ require "sinatra/base"
3
+ require "rrd"
4
+ require "active_support/all"
5
+
6
+ class GangliaJSCharts
7
+ class << self
8
+ attr_accessor :charts_root
9
+ end
10
+
11
+ class Server < Sinatra::Base
12
+ set :public, File.expand_path(File.dirname(__FILE__) + "/../public")
13
+ set :dump_errors, true
14
+
15
+ get "/clusters.json" do
16
+ Dir[charts_root + "/*"].select { |f| File.directory?(f) }.map { |f| f.gsub(charts_root, "").delete("/") }.to_json
17
+ end
18
+
19
+ get "/:cluster/servers.json" do
20
+ cluster_root = charts_root + "/#{params[:cluster]}"
21
+ Dir["#{cluster_root}/*"].select { |f| File.directory?(f) }.map { |f| f.gsub(cluster_root, "").delete("/") }.to_json
22
+ end
23
+
24
+ get "/:cluster/:server/metrics.json" do
25
+ server_root = charts_root + "/#{params[:cluster]}/#{params[:server]}"
26
+ Dir["#{server_root}/*"].reject { |f| File.directory?(f) }.map { |f| f.gsub(server_root, "").delete("/").gsub(".rrd", "") }.to_json
27
+ end
28
+
29
+ get "/:cluster/:server/:metric.json" do
30
+ # NOTE: This is dangerous, but this isn't designed to be public-facing
31
+ length, unit = params[:time_window].split(".")
32
+ start = length.to_i.send(unit).ago
33
+
34
+ rrd = RRD::Base.new(charts_root + "/" + [params[:cluster], params[:server], params[:metric]].join("/") + ".rrd")
35
+ results = rrd.fetch(:average, :start => start.to_i, :end => Time.new.to_i)
36
+ results[1..results.length].map { |n| n.last.nan? ? [n.first, 0] : n }.to_json
37
+ end
38
+
39
+ get "/" do
40
+ File.read(File.expand_path(File.dirname(__FILE__) + "/../public/index.html"))
41
+ end
42
+
43
+ get "/graphs.json" do
44
+ if File.exist?(charts_root + "/graphs.json")
45
+ File.read(charts_root + "/graphs.json")
46
+ else
47
+ ""
48
+ end
49
+ end
50
+
51
+ post "/graphs.json" do
52
+ File.open(charts_root + "/graphs.json", "w") { |f| f << params[:json] }
53
+ ""
54
+ end
55
+
56
+ private
57
+ def charts_root
58
+ GangliaJSCharts.charts_root
59
+ end
60
+ end
61
+ end
Binary file
@@ -0,0 +1,225 @@
1
+ Function.prototype.bind = function(object) {
2
+ var method = this;
3
+ return function() {
4
+ return method.apply(object, arguments);
5
+ };
6
+ };
7
+
8
+ var viewModel = {
9
+ timeWindow: ko.observable("1.hour"),
10
+ clusters: ko.observableArray([]),
11
+ servers: ko.observableArray([]),
12
+ metrics: ko.observableArray([]),
13
+ selectedCluster: ko.observable(""),
14
+ selectedServer: ko.observable(""),
15
+ selectedMetric: ko.observable(""),
16
+ currentGraph: ko.observableArray([]),
17
+ savedGraphs: ko.observableArray([]),
18
+ graphTitle: ko.observable(""),
19
+ savingSavedGraphs: ko.observable(false),
20
+ selectSavedGraph: function(i) {
21
+ var graph = this.savedGraphs()[i];
22
+ this.clearGraph();
23
+ this.graphTitle(graph.name);
24
+ _.each(graph.metrics, function(metric) {
25
+ this.add(metric);
26
+ }.bind(this));
27
+ },
28
+ saveCurrentGraph: function() {
29
+ this.savedGraphs.push({name: this.graphTitle(), metrics: this.currentGraph()});
30
+ this.drawGraphIfReady();
31
+ },
32
+ removeSavedGraph: function(i) {
33
+ if (confirm("Are you sure?")) {
34
+ var graph = this.savedGraphs()[i];
35
+ this.savedGraphs.remove(graph);
36
+ }
37
+ },
38
+ clearGraph: function() {
39
+ for(var i = 0; i < this.currentGraph().length; i++)
40
+ this.remove(i);
41
+ },
42
+ scale: function(i) {
43
+ var metric = this.currentGraph()[i];
44
+ metric.scale = !metric.scale;
45
+ this.currentGraph.valueHasMutated();
46
+
47
+ this.drawGraphIfReady();
48
+ },
49
+ remove: function(i) {
50
+ var metric = this.currentGraph()[i];
51
+ this.currentGraph.remove(metric);
52
+ this.drawGraphIfReady();
53
+ },
54
+ fetchClusters: function() {
55
+ $.getJSON("/clusters.json", function(data) {
56
+ $.each(data, function(i, e) { this.clusters.push(e); }.bind(this));
57
+ }.bind(this));
58
+ },
59
+ clusterSelected: function() {
60
+ var cluster = this.selectedCluster();
61
+
62
+ $.getJSON("/" + cluster + "/servers.json", function(data) {
63
+ $.each(data, function(i, e) { this.servers.push(e); }.bind(this));
64
+ }.bind(this));
65
+ },
66
+ serverSelected: function() {
67
+ var cluster = this.selectedCluster();
68
+ var server = this.selectedServer();
69
+
70
+ $.getJSON("/" + cluster + "/" + server + "/metrics.json", function(data) {
71
+ $.each(data, function(i, e) { this.metrics.push(e); }.bind(this));
72
+ }.bind(this));
73
+ },
74
+ addToGraph: function() {
75
+ var name = [this.selectedCluster(),
76
+ this.selectedServer(),
77
+ this.selectedMetric()].join("/");
78
+ this.add({name: name});
79
+ },
80
+ getMetricByName: function(name) {
81
+ return _.detect(this.currentGraph(), function(metric) {
82
+ return metric.name == metric;
83
+ });
84
+ },
85
+ add: function(metric) {
86
+ if (!this.getMetricByName(metric.name)) {
87
+ this.currentGraph.push(metric);
88
+ this.fetchData(metric);
89
+ }
90
+ },
91
+ fetchData: function(metric) {
92
+ if (metric.data) {
93
+ this.drawGraph();
94
+ } else {
95
+ $.getJSON("/" + metric.name + ".json?time_window=" + this.timeWindow(), function(data) {
96
+ metric.data = data;
97
+ this.drawGraphIfReady();
98
+ }.bind(this));
99
+ }
100
+ },
101
+ timeChanged: function() {
102
+ this.resetChartIfExists();
103
+
104
+ _.each(this.currentGraph(), function(metric) {
105
+ delete metric.data;
106
+ this.fetchData(metric);
107
+ }.bind(this));
108
+ },
109
+ readyToDrawGraph: function() {
110
+ return _.all(this.currentGraph(), function(metric) {
111
+ return metric.data;
112
+ }.bind(this));
113
+ },
114
+ drawGraphIfReady: function() {
115
+ if (this.readyToDrawGraph()) {
116
+ this.drawGraph();
117
+ }
118
+ },
119
+ resetChartIfExists: function() {
120
+ if (this.chart) {
121
+ this.chart.destroy();
122
+ this.chart = null;
123
+ }
124
+ },
125
+ drawGraph: function() {
126
+ this.resetChartIfExists();
127
+
128
+ if (this.currentGraph().length == 0) return;
129
+
130
+ var series = [];
131
+ _.each(this.currentGraph(), function(metric) {
132
+ var data = metric.data;
133
+ var interval = data[1][0] - data[0][0];
134
+
135
+ if (metric.scale) {
136
+ data = $.map(data, function(point) {
137
+ return [[point[0], point[1] * 60]];
138
+ });
139
+ }
140
+
141
+ series.push({
142
+ type: 'area',
143
+ name: metric.name,
144
+ pointInterval: interval * 1000,
145
+ pointStart: data[0][0] * 1000,
146
+ data: $.map(data, function(e) { return e[1]; })
147
+ });
148
+ });
149
+
150
+ this.chart = new Highcharts.Chart({
151
+ chart: {
152
+ renderTo: 'chart',
153
+ zoomType: 'x',
154
+ spacingRight: 20
155
+ },
156
+ title: {
157
+ text: this.graphTitle()
158
+ },
159
+ subtitle: {
160
+ text: document.ontouchstart === undefined ?
161
+ 'Click and drag in the plot area to zoom in' :
162
+ 'Drag your finger over the plot to zoom in'
163
+ },
164
+ xAxis: {
165
+ type: 'datetime',
166
+ maxZoom: 360,
167
+ title: {
168
+ text: null
169
+ }
170
+ },
171
+ yAxis: {
172
+ title: {
173
+ text: ''
174
+ },
175
+ startOnTick: false,
176
+ showFirstLabel: false
177
+ },
178
+ tooltip: {
179
+ shared: true
180
+ },
181
+ legend: {
182
+ enabled: false
183
+ },
184
+ series: series
185
+ });
186
+ },
187
+ savedGraphsChanged: function() {
188
+ this.savingSavedGraphs(true);
189
+ var graphs = _.map(this.savedGraphs(), function(graph) {
190
+ return {name: graph.name, metrics: _.map(graph.metrics, function(metric) {
191
+ return {
192
+ name: metric.name,
193
+ scale: metric.scale
194
+ }
195
+ })};
196
+ });
197
+
198
+ $.ajax({
199
+ type: "post",
200
+ dataType: "json",
201
+ url: "/graphs.json",
202
+ data: {"json": JSON.stringify(graphs)},
203
+ success: function() {
204
+ this.savingSavedGraphs(false);
205
+ }.bind(this)
206
+ });
207
+ }
208
+ };
209
+
210
+ $(function() {
211
+ ko.applyBindings(viewModel);
212
+
213
+ viewModel.selectedCluster.subscribe(viewModel.clusterSelected, viewModel);
214
+ viewModel.selectedServer.subscribe(viewModel.serverSelected, viewModel);
215
+ viewModel.timeWindow.subscribe(viewModel.timeChanged, viewModel);
216
+ viewModel.savedGraphs.subscribe(viewModel.savedGraphsChanged, viewModel);
217
+
218
+ $.getJSON("/graphs.json", function(graphs) {
219
+ _.each(graphs, function(graph) {
220
+ viewModel.savedGraphs.push(graph);
221
+ });
222
+ });
223
+
224
+ viewModel.fetchClusters();
225
+ });