perus 0.1.11 → 0.1.12
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/exe/perus-console +24 -0
- data/lib/perus/server.rb +1 -0
- data/lib/perus/server/app.rb +49 -18
- data/lib/perus/server/db.rb +42 -4
- data/lib/perus/server/public/css/style.css +23 -4
- data/lib/perus/server/server.rb +18 -1
- data/lib/perus/server/stats.rb +126 -0
- data/lib/perus/server/views/layout.erb +1 -0
- data/lib/perus/server/views/stats.erb +31 -0
- data/lib/perus/server/views/systems.erb +28 -16
- data/lib/perus/version.rb +1 -1
- metadata +6 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 820c3e7205d8bb9dcceb2fd9d52ceeb76db9e39d
|
4
|
+
data.tar.gz: 2ae3ddcda6b97e2c21c01d1fadef7dda294a7c62
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 968cf198e14eec35f27faf8b8f6e6a090524c1e6b5b9540197090449d570464dbcf2b113d4165fb3da4d895a37bd4439959b3a61a934c05dcbbcf44aa55ce961
|
7
|
+
data.tar.gz: 31080180df8b4553f3b6cf2cc5073b830fdea47a95874cd10a784fbd35214297c8683bf571f150e2304ecc426987e67945c74529233d87d6e67fb0c0df07d517
|
data/exe/perus-console
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'perus'
|
3
|
+
require 'optparse'
|
4
|
+
require 'irb'
|
5
|
+
|
6
|
+
options_path = Perus::Server::DEFAULT_SERVER_OPTIONS_PATH
|
7
|
+
|
8
|
+
OptionParser.new do |opts|
|
9
|
+
opts.banner = "Usage: perus-console [options]"
|
10
|
+
|
11
|
+
opts.on('-c', '--config PATH', "Path to config file (default: #{Perus::Server::DEFAULT_SERVER_OPTIONS_PATH})") do |c|
|
12
|
+
options_path = c
|
13
|
+
end
|
14
|
+
|
15
|
+
opts.on('-h', '--help', 'Prints this help') do
|
16
|
+
puts opts
|
17
|
+
exit
|
18
|
+
end
|
19
|
+
end.parse!
|
20
|
+
|
21
|
+
include Perus::Server
|
22
|
+
Server.load_options(options_path)
|
23
|
+
DB.start
|
24
|
+
IRB.start
|
data/lib/perus/server.rb
CHANGED
data/lib/perus/server/app.rb
CHANGED
@@ -102,6 +102,11 @@ module Perus::Server
|
|
102
102
|
redirect "#{url_prefix}admin/configs/#{params['config_id']}"
|
103
103
|
end
|
104
104
|
|
105
|
+
get '/admin/stats' do
|
106
|
+
@stats = Stats.new
|
107
|
+
erb :stats
|
108
|
+
end
|
109
|
+
|
105
110
|
|
106
111
|
#----------------------
|
107
112
|
# API
|
@@ -142,30 +147,33 @@ module Perus::Server
|
|
142
147
|
|
143
148
|
# receive data from a client
|
144
149
|
post '/systems/:id/ping' do
|
145
|
-
|
150
|
+
Server.ping_queue.post do
|
151
|
+
timestamp = Time.now.to_i
|
146
152
|
|
147
|
-
|
148
|
-
|
149
|
-
|
153
|
+
# update the system with its last known ip and update time
|
154
|
+
system = System.with_pk!(params['id'])
|
155
|
+
system.last_updated = timestamp
|
150
156
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
157
|
+
if request.ip == '127.0.0.1' && ENV['RACK_ENV'] == 'production'
|
158
|
+
system.ip = request.env['HTTP_X_FORWARDED_FOR']
|
159
|
+
else
|
160
|
+
system.ip = request.ip
|
161
|
+
end
|
162
|
+
|
163
|
+
# errors is either nil or a hash of the format - module: [err, ...]
|
164
|
+
system.save_metric_errors(params, timestamp)
|
156
165
|
|
157
|
-
|
158
|
-
|
166
|
+
# add each new value, a later process cleans up old values
|
167
|
+
system.save_values(params, timestamp)
|
159
168
|
|
160
|
-
|
161
|
-
|
169
|
+
# save action return values and prevent them from running again
|
170
|
+
system.save_actions(params, timestamp)
|
162
171
|
|
163
|
-
|
164
|
-
|
172
|
+
# ip, last updated, uploads and metrics are now updated. these are
|
173
|
+
# stored on the system.
|
174
|
+
system.save
|
175
|
+
end
|
165
176
|
|
166
|
-
# ip, last updated, uploads and metrics are now updated. these are
|
167
|
-
# stored on the system.
|
168
|
-
system.save
|
169
177
|
content_type :json
|
170
178
|
{success: true}.to_json
|
171
179
|
end
|
@@ -220,6 +228,19 @@ module Perus::Server
|
|
220
228
|
redirect "#{url_prefix}groups/#{params['id']}/systems"
|
221
229
|
end
|
222
230
|
|
231
|
+
# delete completed actions in a group
|
232
|
+
delete '/groups/:id/systems/actions' do
|
233
|
+
group = Group.with_pk!(params['id'])
|
234
|
+
group.systems.each do |system|
|
235
|
+
system.actions.each do |action|
|
236
|
+
next if action.timestamp.nil?
|
237
|
+
action.destroy
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
redirect "#{url_prefix}groups/#{params['id']}/systems"
|
242
|
+
end
|
243
|
+
|
223
244
|
# create an action for all systems
|
224
245
|
post '/systems/actions' do
|
225
246
|
System.each do |system|
|
@@ -229,6 +250,16 @@ module Perus::Server
|
|
229
250
|
redirect "#{url_prefix}systems"
|
230
251
|
end
|
231
252
|
|
253
|
+
# delete all completed actions
|
254
|
+
delete '/systems/actions' do
|
255
|
+
Action.each do |action|
|
256
|
+
next if action.timestamp.nil?
|
257
|
+
action.destroy
|
258
|
+
end
|
259
|
+
|
260
|
+
redirect "#{url_prefix}systems"
|
261
|
+
end
|
262
|
+
|
232
263
|
# delete an action. deletion also clears any uploaded files.
|
233
264
|
delete '/systems/:system_id/actions/:id' do
|
234
265
|
action = Action.with_pk!(params['id'])
|
data/lib/perus/server/db.rb
CHANGED
@@ -4,6 +4,8 @@ require 'concurrent'
|
|
4
4
|
|
5
5
|
module Perus::Server
|
6
6
|
module DB
|
7
|
+
MAX_VACUUM_ATTEMPTS = 5
|
8
|
+
|
7
9
|
def self.db
|
8
10
|
@db
|
9
11
|
end
|
@@ -42,7 +44,31 @@ module Perus::Server
|
|
42
44
|
# restructures the db so system records in the values index should
|
43
45
|
# be sequentially stored
|
44
46
|
vacuum_task = Concurrent::TimerTask.new do
|
45
|
-
|
47
|
+
attempts = 0
|
48
|
+
complete = false
|
49
|
+
|
50
|
+
while !complete && attempts < MAX_VACUUM_ATTEMPTS
|
51
|
+
begin
|
52
|
+
puts "Vacuuming, attempt #{attempts + 1}"
|
53
|
+
start = Time.now
|
54
|
+
@db.execute('vacuum')
|
55
|
+
Stats.vacuumed!(Time.now - start)
|
56
|
+
complete = true
|
57
|
+
puts "Vacuuming complete"
|
58
|
+
|
59
|
+
rescue
|
60
|
+
attempts += 1
|
61
|
+
if attempts < MAX_VACUUM_ATTEMPTS
|
62
|
+
puts "Vacuum failed, will reattempt after short sleep"
|
63
|
+
sleep(5)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
if !complete
|
69
|
+
puts "Vacuum failed more than MAX_VACUUM_ATTEMPTS"
|
70
|
+
Stats.vacuumed!('failed')
|
71
|
+
end
|
46
72
|
end
|
47
73
|
|
48
74
|
# fire every 12 hours
|
@@ -54,7 +80,13 @@ module Perus::Server
|
|
54
80
|
# are removed from a system, the accompanying metric record is also
|
55
81
|
# removed.
|
56
82
|
cleanup_task = Concurrent::TimerTask.new do
|
57
|
-
|
83
|
+
begin
|
84
|
+
start = Time.now
|
85
|
+
Perus::Server::DB.cleanup
|
86
|
+
Stats.cleaned!(Time.now - start)
|
87
|
+
rescue
|
88
|
+
Stats.cleaned!('failed')
|
89
|
+
end
|
58
90
|
end
|
59
91
|
|
60
92
|
# fire every hour
|
@@ -66,10 +98,16 @@ module Perus::Server
|
|
66
98
|
# cached so lookups can be done against the db, rather than running
|
67
99
|
# each alert for each system on a page load.
|
68
100
|
cache_alerts_task = Concurrent::TimerTask.new do
|
69
|
-
|
101
|
+
begin
|
102
|
+
start = Time.now
|
103
|
+
Perus::Server::Alert.cache_active_alerts
|
104
|
+
Stats.alerts_cached!(Time.now - start)
|
105
|
+
rescue
|
106
|
+
Stats.alerts_cached!('failed')
|
107
|
+
end
|
70
108
|
end
|
71
109
|
|
72
|
-
cache_alerts_task.execution_interval = Server.options.
|
110
|
+
cache_alerts_task.execution_interval = Server.options.cache_alerts_secs
|
73
111
|
cache_alerts_task.execute
|
74
112
|
end
|
75
113
|
|
@@ -292,25 +292,25 @@ main {
|
|
292
292
|
width: 450px;
|
293
293
|
}
|
294
294
|
|
295
|
-
|
295
|
+
dl {
|
296
296
|
display: inline-block;
|
297
297
|
vertical-align: top;
|
298
298
|
width: 450px;
|
299
299
|
}
|
300
300
|
|
301
|
-
|
301
|
+
dt, dd {
|
302
302
|
display: inline-block;
|
303
303
|
vertical-align: top;
|
304
304
|
margin-bottom: 10px;
|
305
305
|
font-size: 13px;
|
306
306
|
}
|
307
307
|
|
308
|
-
|
308
|
+
dt {
|
309
309
|
width: 100px;
|
310
310
|
font-weight: 600;
|
311
311
|
}
|
312
312
|
|
313
|
-
|
313
|
+
dd {
|
314
314
|
width: 340px;
|
315
315
|
line-height: 16px;
|
316
316
|
}
|
@@ -507,3 +507,22 @@ article.graph {
|
|
507
507
|
padding: 15px 20px;
|
508
508
|
border-radius: 4px;
|
509
509
|
}
|
510
|
+
|
511
|
+
#actions-menu {
|
512
|
+
}
|
513
|
+
|
514
|
+
#actions-menu p {
|
515
|
+
display: inline-block;
|
516
|
+
float: left;
|
517
|
+
}
|
518
|
+
|
519
|
+
#actions-menu form {
|
520
|
+
display: inline-block;
|
521
|
+
float: right;
|
522
|
+
margin-top: 30px;
|
523
|
+
}
|
524
|
+
|
525
|
+
h2.stats {
|
526
|
+
margin-bottom: 10px;
|
527
|
+
margin-top: 20px;
|
528
|
+
}
|
data/lib/perus/server/server.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'concurrent'
|
1
2
|
require 'thin'
|
2
3
|
|
3
4
|
DEFAULT_SERVER_OPTIONS = {
|
@@ -10,7 +11,8 @@ DEFAULT_SERVER_OPTIONS = {
|
|
10
11
|
'site_name' => 'Perus',
|
11
12
|
'url_prefix' => '/',
|
12
13
|
'keep_hours' => 24,
|
13
|
-
'
|
14
|
+
'cache_alerts_secs' => 20,
|
15
|
+
'stats_path' => '/var/perus.stats'
|
14
16
|
}
|
15
17
|
}
|
16
18
|
|
@@ -19,6 +21,8 @@ module Perus::Server
|
|
19
21
|
def initialize(options_path = DEFAULT_SERVER_OPTIONS_PATH, environment = 'development')
|
20
22
|
self.class.load_options(options_path)
|
21
23
|
ENV['RACK_ENV'] = environment
|
24
|
+
|
25
|
+
# initialise/migrate the db and start cleanup/caching timers
|
22
26
|
DB.start
|
23
27
|
DB.start_timers
|
24
28
|
end
|
@@ -31,6 +35,19 @@ module Perus::Server
|
|
31
35
|
)
|
32
36
|
end
|
33
37
|
|
38
|
+
def self.ping_queue
|
39
|
+
# ping data is processed in a thread pool
|
40
|
+
@ping_queue ||= Concurrent::ThreadPoolExecutor.new(
|
41
|
+
min_threads: 2,
|
42
|
+
max_threads: 2,
|
43
|
+
max_queue: 0
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.shutdown
|
48
|
+
@ping_queue.shutdown
|
49
|
+
end
|
50
|
+
|
34
51
|
def self.options
|
35
52
|
@options ||= Perus::Options.new
|
36
53
|
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Perus::Server
|
4
|
+
class Stats
|
5
|
+
MAX_HISTORY = 10
|
6
|
+
attr_reader :data
|
7
|
+
|
8
|
+
# data format (json):
|
9
|
+
# {
|
10
|
+
# "vacuums": [
|
11
|
+
# ["ISO time", duration (int) || 'failed'],
|
12
|
+
# ...
|
13
|
+
# ]
|
14
|
+
# }
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
if File.exists?(Server.options.stats_path)
|
18
|
+
data = IO.read(Server.options.stats_path)
|
19
|
+
unless data.empty?
|
20
|
+
@data = JSON.parse(data)
|
21
|
+
return
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
@data = {
|
26
|
+
'vacuums' => [],
|
27
|
+
'alerts_caches' => [],
|
28
|
+
'cleans' => []
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def save
|
33
|
+
IO.write(Server.options.stats_path, JSON.dump(@data))
|
34
|
+
end
|
35
|
+
|
36
|
+
# vacuuming
|
37
|
+
def last_vacuum
|
38
|
+
entry = @data['vacuums'].last
|
39
|
+
entry ? entry.first : nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def vacuum_time
|
43
|
+
entry = @data['vacuums'].last
|
44
|
+
entry ? entry.last : nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def average_vacuum_time
|
48
|
+
entries = @data['vacuums']
|
49
|
+
return nil if entries.empty?
|
50
|
+
ints = entries.map(&:last).select {|time| time.is_a?(Numeric)}
|
51
|
+
ints.reduce(:+).to_f / ints.length
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.vacuumed!(time)
|
55
|
+
stats = Stats.new
|
56
|
+
list = stats.data['vacuums']
|
57
|
+
list << [Time.now.to_s, time]
|
58
|
+
|
59
|
+
if list.length > MAX_HISTORY
|
60
|
+
list.drop(list.length - MAX_HISTORY)
|
61
|
+
end
|
62
|
+
|
63
|
+
stats.save
|
64
|
+
end
|
65
|
+
|
66
|
+
# alerts caching
|
67
|
+
def last_alerts_cache
|
68
|
+
entry = @data['alerts_caches'].last
|
69
|
+
entry ? entry.first : nil
|
70
|
+
end
|
71
|
+
|
72
|
+
def alerts_cache_time
|
73
|
+
entry = @data['alerts_caches'].last
|
74
|
+
entry ? entry.last : nil
|
75
|
+
end
|
76
|
+
|
77
|
+
def average_alerts_cache_time
|
78
|
+
entries = @data['alerts_caches']
|
79
|
+
return nil if entries.empty?
|
80
|
+
ints = entries.map(&:last).select {|time| time.is_a?(Numeric)}
|
81
|
+
ints.reduce(:+).to_f / ints.length
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.alerts_cached!(time)
|
85
|
+
stats = Stats.new
|
86
|
+
list = stats.data['alerts_caches']
|
87
|
+
list << [Time.now.to_s, time]
|
88
|
+
|
89
|
+
if list.length > MAX_HISTORY
|
90
|
+
list.drop(list.length - MAX_HISTORY)
|
91
|
+
end
|
92
|
+
|
93
|
+
stats.save
|
94
|
+
end
|
95
|
+
|
96
|
+
# cleaning
|
97
|
+
def last_clean
|
98
|
+
entry = @data['cleans'].last
|
99
|
+
entry ? entry.first : nil
|
100
|
+
end
|
101
|
+
|
102
|
+
def clean_time
|
103
|
+
entry = @data['cleans'].last
|
104
|
+
entry ? entry.last : nil
|
105
|
+
end
|
106
|
+
|
107
|
+
def average_clean_time
|
108
|
+
entries = @data['cleans']
|
109
|
+
return nil if entries.empty?
|
110
|
+
ints = entries.map(&:last).select {|time| time.is_a?(Numeric)}
|
111
|
+
ints.reduce(:+).to_f / ints.length
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.cleaned!(time)
|
115
|
+
stats = Stats.new
|
116
|
+
list = stats.data['cleans']
|
117
|
+
list << [Time.now.to_s, time]
|
118
|
+
|
119
|
+
if list.length > MAX_HISTORY
|
120
|
+
list.drop(list.length - MAX_HISTORY)
|
121
|
+
end
|
122
|
+
|
123
|
+
stats.save
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -31,6 +31,7 @@
|
|
31
31
|
<%= nav_item("#{url_prefix}admin/configs", 'Configs') %>
|
32
32
|
<%= nav_item("#{url_prefix}admin/alerts", 'Alerts') %>
|
33
33
|
<%= nav_item("#{url_prefix}admin/scripts", 'Scripts') %>
|
34
|
+
<%= nav_item("#{url_prefix}admin/stats", 'Stats') %>
|
34
35
|
</ul>
|
35
36
|
<% end %>
|
36
37
|
</li>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<h1>Background Stats</h1>
|
2
|
+
|
3
|
+
<h2 class="stats">Vacuuming</h2>
|
4
|
+
<dl>
|
5
|
+
<dt>Last Vacuum</dt>
|
6
|
+
<dd><%= @stats.last_vacuum || 'nil' %></dd>
|
7
|
+
<dt>Vacuum Time</dt>
|
8
|
+
<dd><%= "%0.4f" % @stats.vacuum_time rescue 'nil' %></dd>
|
9
|
+
<dt>Average Vacuum Time</dt>
|
10
|
+
<dd><%= "%0.4f" % @stats.average_vacuum_time rescue 'nil' %></dd>
|
11
|
+
</dl>
|
12
|
+
|
13
|
+
<h2 class="stats">Alerts Caching</h2>
|
14
|
+
<dl>
|
15
|
+
<dt>Last Alerts Cache</dt>
|
16
|
+
<dd><%= @stats.last_alerts_cache || 'nil' %></dd>
|
17
|
+
<dt>Alerts Cache Time</dt>
|
18
|
+
<dd><%= "%0.4f" % @stats.alerts_cache_time rescue 'nil' %></dd>
|
19
|
+
<dt>Average Alerts Cache Time</dt>
|
20
|
+
<dd><%= "%0.4f" % @stats.average_alerts_cache_time rescue 'nil' %></dd>
|
21
|
+
</dl>
|
22
|
+
|
23
|
+
<h2 class="stats">Cleaning</h2>
|
24
|
+
<dl>
|
25
|
+
<dt>Last Clean</dt>
|
26
|
+
<dd><%= @stats.last_clean || 'nil' %></dd>
|
27
|
+
<dt>Clean Time</dt>
|
28
|
+
<dd><%= "%0.4f" % @stats.clean_time rescue 'nil' %></dd>
|
29
|
+
<dt>Average Clean Time</dt>
|
30
|
+
<dd><%= "%0.4f" % @stats.average_clean_time rescue 'nil' %></dd>
|
31
|
+
</dl>
|
@@ -17,22 +17,29 @@
|
|
17
17
|
<% end %>
|
18
18
|
<p id="no-systems" <% unless @systems.empty? %>style="display: none"<% end %>>No Systems</p>
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
<
|
23
|
-
|
24
|
-
<
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
</
|
20
|
+
|
21
|
+
<menu id="actions-menu">
|
22
|
+
<p id="add-command">
|
23
|
+
Add Action to <%= @title %>:
|
24
|
+
<select id="command-select">
|
25
|
+
<option></option>
|
26
|
+
<optgroup label="Commands">
|
27
|
+
<% command_actions.each do |command| %>
|
28
|
+
<option value="<%= command.name %>"><%= command.human_name %></option>
|
29
|
+
<% end %>
|
30
|
+
</optgroup>
|
31
|
+
<optgroup label="Scripts">
|
32
|
+
<% @scripts.each do |script| %>
|
33
|
+
<option value="<%= script.code_name %>"><%= script.name %></option>
|
34
|
+
<% end %>
|
35
|
+
</optgroup>
|
36
|
+
</select>
|
37
|
+
</p>
|
38
|
+
<form id="delete-completed" method="POST" action="<%= url_prefix %><%= @action_url %>">
|
39
|
+
<input type="hidden" name="_method" value="DELETE">
|
40
|
+
<input type="submit" value="Delete Completed Actions">
|
41
|
+
</form>
|
42
|
+
</menu>
|
36
43
|
|
37
44
|
<!-- new commands -->
|
38
45
|
<% command_actions.each do |command| %>
|
@@ -48,6 +55,11 @@
|
|
48
55
|
<% end %>
|
49
56
|
|
50
57
|
<script>
|
58
|
+
$('#delete-completed').submit(function(event) {
|
59
|
+
if (!confirm('Are you sure you want to delete all completed actions? Their results will no longer be available.'))
|
60
|
+
event.preventDefault();
|
61
|
+
});
|
62
|
+
|
51
63
|
$('#command-select').change(function() {
|
52
64
|
$('form.command').hide();
|
53
65
|
$('form[data-command="' + $(this).val() + '"]').show();
|
data/lib/perus/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: perus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Will Cannings
|
@@ -196,6 +196,7 @@ description:
|
|
196
196
|
email:
|
197
197
|
- me@willcannings.com
|
198
198
|
executables:
|
199
|
+
- perus-console
|
199
200
|
- perus-pinger
|
200
201
|
- perus-server
|
201
202
|
extensions: []
|
@@ -210,6 +211,7 @@ files:
|
|
210
211
|
- Rakefile
|
211
212
|
- bin/console
|
212
213
|
- bin/setup
|
214
|
+
- exe/perus-console
|
213
215
|
- exe/perus-pinger
|
214
216
|
- exe/perus-server
|
215
217
|
- lib/perus.rb
|
@@ -304,6 +306,7 @@ files:
|
|
304
306
|
- lib/perus/server/public/js/dygraph-combined.js
|
305
307
|
- lib/perus/server/public/js/jquery.js
|
306
308
|
- lib/perus/server/server.rb
|
309
|
+
- lib/perus/server/stats.rb
|
307
310
|
- lib/perus/server/views/admin/edit.erb
|
308
311
|
- lib/perus/server/views/admin/index.erb
|
309
312
|
- lib/perus/server/views/admin/new.erb
|
@@ -317,6 +320,7 @@ files:
|
|
317
320
|
- lib/perus/server/views/layout.erb
|
318
321
|
- lib/perus/server/views/scripts/edit.erb
|
319
322
|
- lib/perus/server/views/scripts/form.erb
|
323
|
+
- lib/perus/server/views/stats.erb
|
320
324
|
- lib/perus/server/views/system.erb
|
321
325
|
- lib/perus/server/views/systems.erb
|
322
326
|
- lib/perus/server/views/systems/form.erb
|
@@ -347,3 +351,4 @@ signing_key:
|
|
347
351
|
specification_version: 4
|
348
352
|
summary: Simple system overview server
|
349
353
|
test_files: []
|
354
|
+
has_rdoc:
|