pimon 0.1.11 → 0.2.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 (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