perus 0.1.11 → 0.1.12
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|