pimon 0.1.11 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/bin/pimon +26 -19
  3. data/config/config.ru +11 -3
  4. data/config/default.yml +7 -13
  5. data/config/test.yml +6 -12
  6. data/config/test_broken.yml +0 -1
  7. data/lib/pimon.rb +2 -69
  8. data/lib/pimon/app.rb +81 -0
  9. data/lib/pimon/config.rb +34 -0
  10. data/lib/pimon/probe.rb +15 -0
  11. data/lib/pimon/probe/cpu_freq.rb +14 -0
  12. data/lib/pimon/probe/cpu_usage.rb +11 -8
  13. data/lib/pimon/probe/disk_usage.rb +11 -8
  14. data/lib/pimon/probe/memory_usage.rb +15 -10
  15. data/lib/pimon/probe/swap_usage.rb +16 -10
  16. data/lib/pimon/probe/system_memory.rb +16 -16
  17. data/lib/pimon/probe/temperature.rb +11 -13
  18. data/lib/pimon/probe/uptime.rb +37 -9
  19. data/lib/pimon/public/index.js +60 -120
  20. data/lib/pimon/public/normalize.css +427 -0
  21. data/lib/pimon/public/piecon.js +194 -0
  22. data/lib/pimon/public/style.css +13 -0
  23. data/lib/pimon/stats_collector.rb +66 -66
  24. data/lib/pimon/version.rb +1 -1
  25. data/lib/pimon/views/index.haml +17 -7
  26. data/pimon.gemspec +26 -25
  27. data/spec/{pimon_spec.rb → pimon/app_spec.rb} +9 -6
  28. data/spec/pimon/config_spec.rb +33 -0
  29. data/spec/pimon/stats_collector_spec.rb +13 -0
  30. data/spec/spec_helper.rb +9 -2
  31. metadata +195 -219
  32. data/.gitignore +0 -10
  33. data/.travis.yml +0 -6
  34. data/Gemfile +0 -20
  35. data/Gemfile.lock +0 -87
  36. data/README.md +0 -68
  37. data/Rakefile +0 -13
  38. data/WTFPL-LICENSE +0 -13
  39. data/bin/free.c +0 -12
  40. data/bin/makefile +0 -2
  41. data/bin/random.c +0 -21
  42. data/bin/random.h +0 -1
  43. data/bin/vmstat.c +0 -12
  44. data/lib/pimon/hash_extensions.rb +0 -12
  45. data/lib/pimon/pimon_config.rb +0 -32
  46. data/lib/pimon/probe/probe.rb +0 -13
  47. data/lib/pimon/probe/time_check.rb +0 -11
  48. data/lib/pimon/public/piecon.min.js +0 -5
  49. data/lib/pimon/stats.rb +0 -25
  50. data/spec/pimon_config_spec.rb +0 -17
  51. data/spec/stats_collector_spec.rb +0 -67
@@ -0,0 +1,194 @@
1
+ //
2
+ // piecon.js
3
+ //
4
+ // https://github.com/lipka/piecon
5
+ //
6
+ // Copyright (c) 2012 Lukas Lipka <lukaslipka@gmail.com>. All rights reserved.
7
+ //
8
+
9
+ (function(){
10
+ var Piecon = {};
11
+
12
+ var currentFavicon = null;
13
+ var originalFavicon = null;
14
+ var originalTitle = null;
15
+ var canvas = null;
16
+ var options = {};
17
+ var defaults = {
18
+ color: '#ff0084',
19
+ background: '#bbb',
20
+ shadow: '#fff',
21
+ fallback: false
22
+ };
23
+
24
+ var isRetina = window.devicePixelRatio > 1;
25
+
26
+ var ua = (function () {
27
+ var agent = navigator.userAgent.toLowerCase();
28
+ return function (browser) {
29
+ return agent.indexOf(browser) !== -1;
30
+ };
31
+ }());
32
+
33
+ var browser = {
34
+ ie: ua('msie'),
35
+ chrome: ua('chrome'),
36
+ webkit: ua('chrome') || ua('safari'),
37
+ safari: ua('safari') && !ua('chrome'),
38
+ mozilla: ua('mozilla') && !ua('chrome') && !ua('safari')
39
+ };
40
+
41
+ var getFaviconTag = function() {
42
+ var links = document.getElementsByTagName('link');
43
+
44
+ for (var i = 0, l = links.length; i < l; i++) {
45
+ if (links[i].getAttribute('rel') === 'icon' || links[i].getAttribute('rel') === 'shortcut icon') {
46
+ return links[i];
47
+ }
48
+ }
49
+
50
+ return false;
51
+ };
52
+
53
+ var removeFaviconTag = function() {
54
+ // Removed link element will not exist in Nodelist
55
+ var links = Array.prototype.slice.call(document.getElementsByTagName('link'));
56
+ var head = document.getElementsByTagName('head')[0];
57
+
58
+ for (var i = 0, l = links.length; i < l; i++) {
59
+ if (links[i].getAttribute('rel') === 'icon' || links[i].getAttribute('rel') === 'shortcut icon') {
60
+ head.removeChild(links[i]);
61
+ }
62
+ }
63
+ };
64
+
65
+ var setFaviconTag = function(url) {
66
+ removeFaviconTag();
67
+
68
+ var link = document.createElement('link');
69
+ link.type = 'image/x-icon';
70
+ link.rel = 'icon';
71
+ link.href = url;
72
+
73
+ document.getElementsByTagName('head')[0].appendChild(link);
74
+ };
75
+
76
+ var getCanvas = function () {
77
+ if (!canvas) {
78
+ canvas = document.createElement("canvas");
79
+ if (isRetina) {
80
+ canvas.width = 32;
81
+ canvas.height = 32;
82
+ } else {
83
+ canvas.width = 16;
84
+ canvas.height = 16;
85
+ }
86
+ }
87
+
88
+ return canvas;
89
+ };
90
+
91
+ var drawFavicon = function(percentage) {
92
+ var canvas = getCanvas();
93
+ var context = canvas.getContext("2d");
94
+ var percentage = percentage || 0;
95
+ var src = currentFavicon;
96
+
97
+ var faviconImage = new Image();
98
+ faviconImage.onload = function() {
99
+ if (context) {
100
+ context.clearRect(0, 0, canvas.width, canvas.height);
101
+
102
+ // Draw shadow
103
+ context.beginPath();
104
+ context.moveTo(canvas.width / 2, canvas.height / 2);
105
+ context.arc(canvas.width / 2, canvas.height / 2, Math.min(canvas.width / 2, canvas.height / 2), 0, Math.PI * 2, false);
106
+ context.fillStyle = options.shadow;
107
+ context.fill();
108
+
109
+ // Draw background
110
+ context.beginPath();
111
+ context.moveTo(canvas.width / 2, canvas.height / 2);
112
+ context.arc(canvas.width / 2, canvas.height / 2, Math.min(canvas.width / 2, canvas.height / 2) - 2, 0, Math.PI * 2, false);
113
+ context.fillStyle = options.background;
114
+ context.fill();
115
+
116
+ // Draw pie
117
+ if (percentage > 0) {
118
+ context.beginPath();
119
+ context.moveTo(canvas.width / 2, canvas.height / 2);
120
+ context.arc(canvas.width / 2, canvas.height / 2, Math.min(canvas.width / 2, canvas.height / 2) - 2, (-0.5) * Math.PI, (-0.5 + 2 * percentage / 100) * Math.PI, false);
121
+ context.lineTo(canvas.width / 2, canvas.height / 2);
122
+ context.fillStyle = options.color;
123
+ context.fill();
124
+ }
125
+
126
+ setFaviconTag(canvas.toDataURL());
127
+ }
128
+ };
129
+
130
+ // allow cross origin resource requests if the image is not a data:uri
131
+ // as detailed here: https://github.com/mrdoob/three.js/issues/1305
132
+ if (!src.match(/^data/)) {
133
+ faviconImage.crossOrigin = 'anonymous';
134
+ }
135
+
136
+ faviconImage.src = src;
137
+ };
138
+
139
+ var updateTitle = function(percentage) {
140
+ if (percentage > 0) {
141
+ document.title = '(' + percentage + '%) ' + originalTitle;
142
+ } else {
143
+ document.title = originalTitle;
144
+ }
145
+ };
146
+
147
+ Piecon.setOptions = function(custom) {
148
+ options = {};
149
+
150
+ for (var key in defaults){
151
+ options[key] = custom.hasOwnProperty(key) ? custom[key] : defaults[key];
152
+ }
153
+
154
+ return this;
155
+ };
156
+
157
+ Piecon.setProgress = function(percentage) {
158
+ if (!originalTitle) {
159
+ originalTitle = document.title;
160
+ }
161
+
162
+ if (!originalFavicon || !currentFavicon) {
163
+ var tag = getFaviconTag();
164
+ originalFavicon = currentFavicon = tag ? tag.getAttribute('href') : '/favicon.ico';
165
+ }
166
+
167
+ if (!isNaN(parseFloat(percentage)) && isFinite(percentage)) {
168
+ if (!getCanvas().getContext || browser.ie || browser.safari || options.fallback === true) {
169
+ // Fallback to updating the browser title if unsupported
170
+ return updateTitle(percentage);
171
+ } else if (options.fallback === 'force') {
172
+ updateTitle(percentage);
173
+ }
174
+
175
+ return drawFavicon(percentage);
176
+ }
177
+
178
+ return false;
179
+ };
180
+
181
+ Piecon.reset = function() {
182
+ if (originalTitle) {
183
+ document.title = originalTitle;
184
+ }
185
+
186
+ if (originalFavicon) {
187
+ currentFavicon = originalFavicon;
188
+ setFaviconTag(currentFavicon);
189
+ }
190
+ };
191
+
192
+ Piecon.setOptions(defaults);
193
+ window.Piecon = Piecon;
194
+ })();
@@ -0,0 +1,13 @@
1
+ .hostname {
2
+ text-align: center;
3
+ }
4
+
5
+ .single_stat {
6
+ padding-left: 20px;
7
+ }
8
+
9
+ .chart {
10
+ display: inline-block;
11
+ min-width: 25%;
12
+ padding: 0 20px;
13
+ }
@@ -1,75 +1,75 @@
1
1
  require 'date'
2
2
  require 'json'
3
- require 'pimon/probe/cpu_usage'
4
- require 'pimon/probe/disk_usage'
5
- require 'pimon/probe/memory_usage'
6
- require 'pimon/probe/swap_usage'
7
- require 'pimon/probe/time_check'
8
- require 'pimon/probe/temperature'
9
- require 'pimon/probe/uptime'
10
- require 'pimon/stats'
11
3
 
12
- class StatsCollector
13
- def initialize(config)
14
- @config = config
15
- @probes = [
16
- Probe::TimeCheck,
17
- Probe::CpuUsage,
18
- Probe::MemoryUsage,
19
- Probe::SwapUsage,
20
- Probe::DiskUsage,
21
- Probe::Temperature,
22
- Probe::Uptime
4
+ require 'pimon/probe'
5
+
6
+ module Pimon
7
+ class StatsCollector
8
+ attr_accessor :config
9
+ attr_accessor :last_update
10
+ attr_accessor :probes
11
+ attr_accessor :probe_threads
12
+ attr_reader :stats
13
+
14
+ CHART_PROBES = [
15
+ Pimon::Probe::CpuUsage,
16
+ Pimon::Probe::MemoryUsage,
17
+ Pimon::Probe::SwapUsage,
18
+ Pimon::Probe::DiskUsage,
19
+ Pimon::Probe::Temperature
23
20
  ]
24
- @stats = Stats.new(@probes.map(&:symbol).concat([:time]))
25
- end
26
-
27
- def collect_stats
28
- pop_old_stats
29
-
30
- @probes.each do |probe|
31
- @stats.push(probe.symbol, probe.check)
21
+
22
+ SINGLE_PROBES = [
23
+ Pimon::Probe::Uptime,
24
+ Pimon::Probe::CpuFreq
25
+ ]
26
+
27
+ def initialize(config)
28
+ @config = config
29
+ @probes = {
30
+ charts: CHART_PROBES,
31
+ single: SINGLE_PROBES
32
+ }
33
+ @stats = { colors: config.colors, charts: {}, single: {}, hostname: config.hostname }
34
+ @probe_threads = []
35
+ @collection_mutex = Mutex.new
32
36
  end
33
- end
34
-
35
- def last_update
36
- time = @stats.index(:time, @config.stats[:number_of_stats] - 1)
37
-
38
- DateTime.parse(time) if time
39
- end
40
-
41
- def show_stats
42
- time = @stats.range(:time)
43
- uptime = @stats.range(:uptime)
44
-
45
- stats = {
46
- :time => { :stats => time.map { |t| (/\d\d:\d\d:\d\d/.match(t))[0] } },
47
- :hostname => @config.hostname,
48
- :uptime => uptime
49
- }
50
-
51
- @probes.each do |probe|
52
- stats[probe.symbol] = {
53
- :stats => @stats.range(probe.symbol),
54
- :color => @config.chart[probe.symbol][:color],
55
- :unit => probe.unit
56
- } unless [:time, :uptime].include?(probe.symbol)
37
+
38
+ def collect_stats
39
+ @collection_mutex.synchronize do
40
+ self.last_update = Time.now
41
+
42
+ [:charts, :single].each do |stats_type|
43
+ probes[stats_type].each do |probe|
44
+ self.probe_threads = []
45
+ probe_threads << Thread.new do
46
+ sample = probe.check(last_update)
47
+ stats[stats_type][sample.probe_name] = sample
48
+ end
49
+ end
50
+ end
51
+ end
57
52
  end
58
-
59
- stats.to_json
60
- end
61
-
62
- private
63
-
64
- def pop_all_queues
65
- @probes.each { |probe| @stats.shift(probe.symbol) }
66
- end
67
-
68
- def pop_old_stats
69
- queue_size = @stats.range(:time).length
70
-
71
- if queue_size >= @config.stats[:number_of_stats]
72
- (queue_size - @config.stats[:number_of_stats] + 1).times { pop_all_queues }
53
+
54
+ def show_stats
55
+ wait_for_stats_collection { stats }
56
+ end
57
+
58
+ def charts
59
+ wait_for_stats_collection { stats[:charts].keys }
60
+ end
61
+
62
+ def single_stats
63
+ wait_for_stats_collection { stats[:single].keys }
64
+ end
65
+
66
+ private
67
+
68
+ def wait_for_stats_collection
69
+ @collection_mutex.synchronize do
70
+ probe_threads.each(&:join)
71
+ yield if block_given?
72
+ end
73
73
  end
74
74
  end
75
75
  end
@@ -1,3 +1,3 @@
1
1
  module Pimon
2
- VERSION = '0.1.11'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  end
@@ -1,12 +1,22 @@
1
1
  %html
2
2
  %header
3
3
  %title Pimon
4
- %script{ :src => '//cdnjs.cloudflare.com/ajax/libs/jquery/1.8.0/jquery-1.8.0.min.js' }
5
- %script{ :src => '//cdnjs.cloudflare.com/ajax/libs/highcharts/2.3.1/highcharts.js' }
6
- %script{ :src => '/piecon.min.js' }
4
+ -#%link{ :href => '/favicon.ico', :rel => :icon, :type => :"image/x-icon" }
5
+ %link{ :href => '/normalize.css', :rel => :stylesheet, :type => :"text/css" }
6
+ %link{ :href => '/style.css', :rel => :stylesheet, :type => :"text/css" }
7
+ %script{ :src => 'https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.0/jquery-1.8.0.min.js' }
8
+ %script{ :src => '/piecon.js' }
9
+ %script{ :src => 'https://cdnjs.cloudflare.com/ajax/libs/smoothie/1.27.0/smoothie.min.js' }
7
10
  %script{ :src => '/index.js' }
11
+
8
12
  %body
9
- #chart
10
- #uptime
11
- Uptime:
12
- %strong.up
13
+ %h1.hostname= hostname
14
+ - single_stats.each do |stat_name|
15
+ .single_stat{ :id => stat_name }
16
+ %strong= "#{stat_name.upcase}: "
17
+ %span.stat
18
+
19
+ - charts.each do |chart_name|
20
+ .chart
21
+ %h2= chart_name.upcase
22
+ %canvas{ :id => "#{chart_name}_chart" }
@@ -1,35 +1,36 @@
1
- # encoding: UTF-8
2
- $: << File.expand_path('./lib')
3
- require 'pimon/version'
1
+ require_relative 'lib/pimon/version'
4
2
 
5
3
  Gem::Specification.new do |s|
6
- s.name = "pimon"
7
- s.version = Pimon::VERSION.dup
4
+ s.name = 'pimon'
5
+ s.version = Pimon::VERSION
6
+ s.licenses = ['WTFPL']
8
7
  s.platform = Gem::Platform::RUBY
9
- s.authors = [ 'Pedro Carriço' ]
10
- s.email = [ 'pedro.carrico@gmail.com' ]
8
+ s.authors = ['Pedro Carriço']
9
+ s.email = ['pedro.carrico@gmail.com']
11
10
  s.homepage = 'http://pimon.pedrocarrico.net/'
12
11
  s.summary = 'Pimon - Raspberry Pi server monitor'
13
12
  s.description = 'Pimon is a simple server monitor designed for the Raspberry Pi.'
14
13
  s.post_install_message = 'Thank you for using Pimon :-)'
15
-
14
+
16
15
  s.required_ruby_version = '>= 1.9'
17
-
18
- s.files = `git ls-files`.split("\n")
19
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
- s.executables = `git ls-files -- bin/pimon`.split("\n").map{ |f| File.basename(f) }
16
+
17
+ s.files = Dir.glob('{lib,config}/**/*') << 'pimon.gemspec'
18
+ s.test_files = Dir.glob('{test,spec,features}/**/*')
19
+ s.executables = 'pimon'
21
20
  s.require_paths = ['lib']
22
-
23
- s.add_runtime_dependency 'haml', '~> 4.0.3'
24
- s.add_runtime_dependency 'sinatra', '~> 1.4.3'
25
- s.add_runtime_dependency 'em-websocket', '= 0.3.8'
26
- s.add_runtime_dependency 'sinatra-websocket', '~> 0.2.1'
27
- s.add_runtime_dependency 'sys-uptime', '~> 0.6.1'
28
- s.add_runtime_dependency 'thin', '~> 1.5.1'
29
-
30
- s.add_development_dependency 'rack-test', '~> 0.6.2'
31
- s.add_development_dependency 'rspec', '~> 2.14.1'
32
- s.add_development_dependency 'simplecov', '~> 0.7.1'
33
- s.add_development_dependency 'simplecov-rcov', '~> 0.2.3'
34
- s.add_development_dependency 'timecop', '~> 0.6.1'
21
+
22
+ s.add_runtime_dependency 'haml', '= 4.0.7'
23
+ s.add_runtime_dependency 'sinatra', '= 1.4.6'
24
+ s.add_runtime_dependency 'faye-websocket', '= 0.10.0'
25
+ s.add_runtime_dependency 'sys-uptime', '= 0.6.2'
26
+ s.add_runtime_dependency 'thin', '= 1.6.3'
27
+
28
+ s.add_development_dependency 'rack-test', '= 0.6.2'
29
+ s.add_development_dependency 'rspec', '= 3.0.0'
30
+ s.add_development_dependency 'rubocop', '=0.33.0'
31
+ s.add_development_dependency 'simplecov', '= 0.10.0'
32
+ s.add_development_dependency 'simplecov-rcov', '= 0.2.3'
33
+ s.add_development_dependency 'timecop', '= 0.7.1'
34
+ s.add_development_dependency 'coveralls', '= 0.8.2'
35
+ s.add_development_dependency 'pry', '= 0.10.0'
35
36
  end