puny-monitor 0.1.0 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +22 -1
- data/app/authorization.rb +27 -0
- data/app/models/bandwidth.rb +2 -2
- data/app/models/cpu_load.rb +3 -3
- data/app/models/disk_io.rb +2 -2
- data/app/puny_monitor.rb +25 -22
- data/app/scheduler.rb +2 -3
- data/app/views/layout.erb +10 -7
- data/lib/puny_monitor/version.rb +1 -1
- data/lib/system_utils.rb +19 -19
- data/public/javascript/Chart.bundle.js +24003 -0
- data/public/javascript/chartkick.js +2570 -0
- data/public/javascript/index.js +2 -4
- metadata +21 -13
- data/.ruby-version +0 -1
- data/Rakefile +0 -50
- data/config.ru +0 -5
- data/db/migrate/20231023000000_add_indices_to_created_at_columns.rb +0 -12
- data/db/schema.rb +0 -61
- data/db/seeds.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5563bcd36e5c112a41100ba5138ca04eb9c2d8ad4cdf7782f9b3847d4d7cc44c
|
4
|
+
data.tar.gz: dcab8591a17194d2a92e0ff23f052d6680348d3925a36f45cef69caf8e5c2130
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e1f2e834bb72b0918f50d80c7a842d8bcdf685d3caddcb28e7aea60808836e90371330cfc2f31d8f479b4414f6db85bdd2883c17dce6cb44db6a47351245a7d5
|
7
|
+
data.tar.gz: ba927bfbd53adfe1aab34ed2b2c0e5cb7452a6128a99cdf61c52d1e6ab2876b59673c24357cf5d6a75defd1830d5a1ad700c2b2124fdbf019d805b796e76bf31
|
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -33,6 +33,20 @@ docker run --rm \
|
|
33
33
|
|
34
34
|
Visit [localhost:4567](http://localhost:4567) to check your system data. To see how to deploy Puny Monitor in a production environment see [Deployment].
|
35
35
|
|
36
|
+
## Authentication
|
37
|
+
|
38
|
+
Puny monitor supports Basic HTTP Authentication. To enable authentication set both the `PUNY_USERNAME` and `PUNY_PASSWORD` environment variables. For example, using with `docker run`:
|
39
|
+
|
40
|
+
```
|
41
|
+
docker run --rm \
|
42
|
+
-v=/:/host:ro,rslave -v=puny-data:/puny-monitor/db \
|
43
|
+
-e HOST_PATH=/host \
|
44
|
+
-e PUNY_USERNAME=admin \
|
45
|
+
-e PUNY_PASSWORD=secret \
|
46
|
+
-p 4567:4567 \
|
47
|
+
hschne/puny-monitor:latest
|
48
|
+
```
|
49
|
+
|
36
50
|
## Deployment
|
37
51
|
|
38
52
|
Puny Monitor was made with [Kamal](https://kamal-deploy.org/) and [Ruby on Rails](https://rubyonrails.org/) in mind. It is recommended that you deploy it as an accessory to your application. Add the following lines to `config/deploy.yml`:
|
@@ -81,7 +95,14 @@ To put it simply, Puny Monitor replicates [Digital Ocean's Monitoring](https://w
|
|
81
95
|
|
82
96
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
83
97
|
|
84
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
98
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To run Puny Monitor locally use
|
99
|
+
```
|
100
|
+
bundle exec rackup
|
101
|
+
```
|
102
|
+
|
103
|
+
Alternatively, you may use the various docker helpers defined in the [Rakefile].
|
104
|
+
|
105
|
+
To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
85
106
|
|
86
107
|
## Contributing
|
87
108
|
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PunyMonitor
|
4
|
+
module Authorization
|
5
|
+
def self.included(base)
|
6
|
+
return unless authorize?
|
7
|
+
|
8
|
+
base.use Rack::Auth::Basic, 'Puny Monitor' do |username, password|
|
9
|
+
username == self.username && password == self.password
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.username
|
14
|
+
@username ||= ENV.fetch('PUNY_USERNAME', nil)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.password
|
18
|
+
@password ||= ENV.fetch('PUNY_PASSWORD', nil)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.authorize?
|
22
|
+
puts username
|
23
|
+
puts password
|
24
|
+
username && password
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/app/models/bandwidth.rb
CHANGED
@@ -4,8 +4,8 @@ class Bandwidth < ActiveRecord::Base
|
|
4
4
|
class << self
|
5
5
|
def average_usage(start_time, group_by)
|
6
6
|
[
|
7
|
-
{
|
8
|
-
{
|
7
|
+
{name: "Incoming Mbps", data: average_for_period(:incoming_mbps, start_time, group_by)},
|
8
|
+
{name: "Outgoing Mbps", data: average_for_period(:outgoing_mbps, start_time, group_by)}
|
9
9
|
]
|
10
10
|
end
|
11
11
|
|
data/app/models/cpu_load.rb
CHANGED
@@ -3,9 +3,9 @@
|
|
3
3
|
class CpuLoad < ActiveRecord::Base
|
4
4
|
def self.average_load(start_time, end_time, group_by)
|
5
5
|
[
|
6
|
-
{
|
7
|
-
{
|
8
|
-
{
|
6
|
+
{name: "1 minute", data: average_for_period(:one_minute, start_time, end_time, group_by)},
|
7
|
+
{name: "5 minutes", data: average_for_period(:five_minutes, start_time, end_time, group_by)},
|
8
|
+
{name: "15 minutes", data: average_for_period(:fifteen_minutes, start_time, end_time, group_by)}
|
9
9
|
]
|
10
10
|
end
|
11
11
|
|
data/app/models/disk_io.rb
CHANGED
@@ -4,8 +4,8 @@ class DiskIO < ActiveRecord::Base
|
|
4
4
|
class << self
|
5
5
|
def average_io(start_time, group_by)
|
6
6
|
[
|
7
|
-
{
|
8
|
-
{
|
7
|
+
{name: "Read MB/s", data: average_for_period(:read_mb_per_sec, start_time, group_by)},
|
8
|
+
{name: "Write MB/s", data: average_for_period(:write_mb_per_sec, start_time, group_by)}
|
9
9
|
]
|
10
10
|
end
|
11
11
|
|
data/app/puny_monitor.rb
CHANGED
@@ -1,16 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require_relative
|
5
|
-
require_relative
|
3
|
+
require 'rufus-scheduler'
|
4
|
+
require_relative 'scheduler'
|
5
|
+
require_relative 'authorization'
|
6
|
+
require_relative '../lib/system_utils'
|
6
7
|
|
7
8
|
module PunyMonitor
|
8
9
|
class App < Sinatra::Base
|
10
|
+
# include Authorization
|
11
|
+
|
9
12
|
configure do
|
10
13
|
register Sinatra::ActiveRecordExtension
|
11
14
|
@scheduler = Rufus::Scheduler.new
|
12
|
-
@scheduler.every(
|
13
|
-
@scheduler.every(
|
15
|
+
@scheduler.every('5s') { Scheduler.collect_data }
|
16
|
+
@scheduler.every('1h') { Scheduler.cleanup_old_data }
|
14
17
|
end
|
15
18
|
|
16
19
|
configure :development do
|
@@ -18,43 +21,43 @@ module PunyMonitor
|
|
18
21
|
end
|
19
22
|
|
20
23
|
set :erb, layout: :layout
|
21
|
-
set :public_folder, File.join(__dir__,
|
22
|
-
set :database_file,
|
24
|
+
set :public_folder, File.join(__dir__, '..', 'public')
|
25
|
+
set :database_file, '../config/database.yml'
|
23
26
|
|
24
|
-
get
|
27
|
+
get '/' do
|
25
28
|
erb :index, locals: { params:, logo: }
|
26
29
|
end
|
27
30
|
|
28
|
-
get
|
31
|
+
get '/up' do
|
29
32
|
200
|
30
33
|
end
|
31
34
|
|
32
|
-
get
|
35
|
+
get '/data/cpu_usage' do
|
33
36
|
content_type :json
|
34
37
|
CpuUsage.average_usage(start_time, group_by).to_json
|
35
38
|
end
|
36
39
|
|
37
|
-
get
|
40
|
+
get '/data/cpu_load' do
|
38
41
|
content_type :json
|
39
42
|
CpuLoad.average_load(start_time, Time.now, group_by).to_json
|
40
43
|
end
|
41
44
|
|
42
|
-
get
|
45
|
+
get '/data/memory_usage' do
|
43
46
|
content_type :json
|
44
47
|
MemoryUsage.average_usage(start_time, group_by).to_json
|
45
48
|
end
|
46
49
|
|
47
|
-
get
|
50
|
+
get '/data/filesystem_usage' do
|
48
51
|
content_type :json
|
49
52
|
FilesystemUsage.average_usage(start_time, group_by).to_json
|
50
53
|
end
|
51
54
|
|
52
|
-
get
|
55
|
+
get '/data/disk_io' do
|
53
56
|
content_type :json
|
54
57
|
DiskIO.average_io(start_time, group_by).to_json
|
55
58
|
end
|
56
59
|
|
57
|
-
get
|
60
|
+
get '/data/bandwidth' do
|
58
61
|
content_type :json
|
59
62
|
Bandwidth.average_usage(start_time, group_by).to_json
|
60
63
|
end
|
@@ -63,28 +66,28 @@ module PunyMonitor
|
|
63
66
|
|
64
67
|
def logo
|
65
68
|
@logo ||= begin
|
66
|
-
file = File.open(
|
69
|
+
file = File.open('public/icon.svg')
|
67
70
|
file.read
|
68
71
|
end
|
69
72
|
end
|
70
73
|
|
71
74
|
def duration
|
72
|
-
params[:duration] ||
|
75
|
+
params[:duration] || '1d'
|
73
76
|
end
|
74
77
|
|
75
78
|
def start_time
|
76
79
|
case duration
|
77
|
-
when
|
78
|
-
when
|
79
|
-
when
|
80
|
-
when
|
80
|
+
when '1h' then 1.hour.ago
|
81
|
+
when '3d' then 3.days.ago
|
82
|
+
when '1w' then 1.week.ago
|
83
|
+
when '1m' then 1.month.ago
|
81
84
|
else 1.day.ago
|
82
85
|
end
|
83
86
|
end
|
84
87
|
|
85
88
|
def group_by
|
86
89
|
case duration
|
87
|
-
when
|
90
|
+
when '1h', '1d' then :minute
|
88
91
|
else :hour
|
89
92
|
end
|
90
93
|
end
|
data/app/scheduler.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require "debug"
|
3
|
+
require_relative '../lib/system_utils'
|
5
4
|
|
6
5
|
module PunyMonitor
|
7
6
|
class Scheduler
|
@@ -25,7 +24,7 @@ module PunyMonitor
|
|
25
24
|
def cleanup_old_data
|
26
25
|
one_month_ago = 1.month.ago
|
27
26
|
[CpuUsage, CpuLoad, MemoryUsage, FilesystemUsage, DiskIO, Bandwidth].each do |model|
|
28
|
-
model.where(
|
27
|
+
model.where('created_at < ?', one_month_ago).delete_all
|
29
28
|
end
|
30
29
|
end
|
31
30
|
end
|
data/app/views/layout.erb
CHANGED
@@ -13,13 +13,16 @@
|
|
13
13
|
<link rel="icon" href="favicon.ico" sizes="32x32">
|
14
14
|
<link rel="icon" href="icon.svg" type="image/svg+xml">
|
15
15
|
|
16
|
-
<script
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
<script type="importmap">
|
17
|
+
{
|
18
|
+
"imports": {
|
19
|
+
"chartkick": "./javascript/chartkick.js",
|
20
|
+
"Chart.bundle": "./javascript/Chart.bundle.js"
|
21
|
+
}
|
22
|
+
}
|
23
|
+
</script>
|
24
|
+
<script type="module" src="javascript/index.js"></script>
|
25
|
+
|
23
26
|
<link
|
24
27
|
rel="preload"
|
25
28
|
href="fonts/Rubik.woff2"
|
data/lib/puny_monitor/version.rb
CHANGED
data/lib/system_utils.rb
CHANGED
@@ -13,9 +13,9 @@ class SystemUtils
|
|
13
13
|
idle = current_cpu[:idle] + current_cpu[:iowait]
|
14
14
|
|
15
15
|
prev_non_idle = prev_cpu[:user] + prev_cpu[:nice] + prev_cpu[:system] +
|
16
|
-
|
16
|
+
prev_cpu[:irq] + prev_cpu[:softirq] + prev_cpu[:steal]
|
17
17
|
non_idle = current_cpu[:user] + current_cpu[:nice] + current_cpu[:system] +
|
18
|
-
|
18
|
+
current_cpu[:irq] + current_cpu[:softirq] + current_cpu[:steal]
|
19
19
|
|
20
20
|
prev_total = prev_idle + prev_non_idle
|
21
21
|
total = idle + non_idle
|
@@ -29,8 +29,8 @@ class SystemUtils
|
|
29
29
|
|
30
30
|
def cpu_load_average
|
31
31
|
File.read("#{proc_path}/loadavg").split.take(3)
|
32
|
-
|
33
|
-
|
32
|
+
.map(&:to_f)
|
33
|
+
.map { |value| value.round(2) }
|
34
34
|
end
|
35
35
|
|
36
36
|
def memory_usage_percent
|
@@ -111,16 +111,16 @@ class SystemUtils
|
|
111
111
|
|
112
112
|
def read_disk_stats
|
113
113
|
primary_disk = File.read("#{proc_path}/partitions")
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
114
|
+
.lines
|
115
|
+
.drop(2)
|
116
|
+
.first
|
117
|
+
.split
|
118
|
+
.last
|
119
119
|
|
120
120
|
stats = File.read("#{proc_path}/diskstats")
|
121
|
-
|
122
|
-
|
123
|
-
|
121
|
+
.lines
|
122
|
+
.map(&:split)
|
123
|
+
.find { |line| line[2] == primary_disk }
|
124
124
|
|
125
125
|
{
|
126
126
|
read_sectors: stats[5].to_i,
|
@@ -130,14 +130,14 @@ class SystemUtils
|
|
130
130
|
|
131
131
|
def read_network_stats
|
132
132
|
primary_interface = File.read("#{proc_path}/net/route")
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
133
|
+
.lines
|
134
|
+
.drop(1)
|
135
|
+
.find { |line| line.split[1] == "00000000" }
|
136
|
+
&.split&.first
|
137
137
|
stats = File.read("#{proc_path}/net/dev")
|
138
|
-
|
139
|
-
|
140
|
-
|
138
|
+
.lines
|
139
|
+
.map(&:split)
|
140
|
+
.find { |line| line[0].chomp(":") == primary_interface }
|
141
141
|
|
142
142
|
{
|
143
143
|
rx_bytes: stats[1].to_i,
|