perus 0.1.7 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/perus/pinger/commands/run_installed_command.rb +11 -0
- data/lib/perus/pinger/commands/service_start.rb +12 -0
- data/lib/perus/pinger/commands/service_stop.rb +12 -0
- data/lib/perus/pinger/commands/upgrade.rb +2 -2
- data/lib/perus/pinger/pinger.rb +24 -0
- data/lib/perus/pinger.rb +3 -0
- data/lib/perus/server/app.rb +22 -25
- data/lib/perus/server/db.rb +28 -12
- data/lib/perus/server/migrations/013_create_active_alerts.rb +14 -0
- data/lib/perus/server/migrations/014_alerts_can_have_errors.rb +13 -0
- data/lib/perus/server/models/action.rb +20 -0
- data/lib/perus/server/models/active_alert.rb +24 -0
- data/lib/perus/server/models/alert.rb +62 -3
- data/lib/perus/server/models/system.rb +10 -0
- data/lib/perus/server/public/css/style.css +53 -12
- data/lib/perus/server/server.rb +8 -2
- data/lib/perus/server/views/index.erb +13 -11
- data/lib/perus/server/views/system.erb +19 -2
- data/lib/perus/server/views/systems.erb +84 -15
- data/lib/perus/version.rb +1 -1
- data/lib/perus.rb +4 -3
- data/perus.gemspec +1 -0
- metadata +22 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c691e0803e6d3cd8e4f71844229b16cbd9c86eff
|
4
|
+
data.tar.gz: 4b50581cb82ee72f16cdc7b0ffe91b03e92d0b5e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 95a1cf16ed2e53948382cd64c2b164b26112cfeec66b22ec298e3cbf6ee6e6d97ffafce83a2ba901f17d84aedfed9d20ff06a71ae4d9498a2564bae5ce715d50
|
7
|
+
data.tar.gz: 74fae7570dd20724b4f79f1cfc943e0fd89d92c7f4306c8e91a11c02f0863e628fd3ab106fe4dd1b14affe51b5f5fdd5bc36ebb36e0fde378fefb4769de39d9f
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Perus::Pinger
|
2
|
+
class RunInstalledCommand < Command
|
3
|
+
description 'Run the command specified with "path". Valid values for
|
4
|
+
"path" are contained in the pinger config file.'
|
5
|
+
option :path, restricted: true
|
6
|
+
|
7
|
+
def run
|
8
|
+
shell(options.path)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Perus::Pinger
|
2
|
+
class ServiceStart < Command
|
3
|
+
description 'Start the init job specified with "job". Valid values
|
4
|
+
for "job" are contained in the pinger config file.'
|
5
|
+
option :job, restricted: true
|
6
|
+
|
7
|
+
def run
|
8
|
+
result = shell("sudo service #{option.job} start")
|
9
|
+
true # shell will capture any errors
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Perus::Pinger
|
2
|
+
class ServiceStop < Command
|
3
|
+
description 'Stop the init job specified with "job". Valid values
|
4
|
+
for "job" are contained in the pinger config file.'
|
5
|
+
option :job, restricted: true
|
6
|
+
|
7
|
+
def run
|
8
|
+
result = shell("sudo service #{option.job} stop")
|
9
|
+
true # shell will capture any errors
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -5,9 +5,9 @@ module Perus::Pinger
|
|
5
5
|
|
6
6
|
def run
|
7
7
|
if options.sudo
|
8
|
-
result = shell('sudo gem
|
8
|
+
result = shell('sudo gem update perus')
|
9
9
|
else
|
10
|
-
result = shell('gem
|
10
|
+
result = shell('gem update perus')
|
11
11
|
end
|
12
12
|
|
13
13
|
result.include?('ERROR') ? result : true
|
data/lib/perus/pinger/pinger.rb
CHANGED
@@ -37,6 +37,30 @@ DEFAULT_PINGER_OPTIONS = {
|
|
37
37
|
|
38
38
|
'KillProcess' => {
|
39
39
|
'process_name' => []
|
40
|
+
},
|
41
|
+
|
42
|
+
'Running' => {
|
43
|
+
'process_path' => []
|
44
|
+
},
|
45
|
+
|
46
|
+
'UpstartStart' => {
|
47
|
+
'job' => []
|
48
|
+
},
|
49
|
+
|
50
|
+
'UpstartStop' => {
|
51
|
+
'job' => []
|
52
|
+
},
|
53
|
+
|
54
|
+
'ServiceStart' => {
|
55
|
+
'job' => []
|
56
|
+
},
|
57
|
+
|
58
|
+
'ServiceStop' => {
|
59
|
+
'job' => []
|
60
|
+
},
|
61
|
+
|
62
|
+
'RunInstalledCommand' => {
|
63
|
+
'path' => []
|
40
64
|
}
|
41
65
|
}
|
42
66
|
|
data/lib/perus/pinger.rb
CHANGED
@@ -17,6 +17,9 @@ module Perus
|
|
17
17
|
require './pinger/commands/sleep'
|
18
18
|
require './pinger/commands/upstart_start'
|
19
19
|
require './pinger/commands/upstart_stop'
|
20
|
+
require './pinger/commands/service_start'
|
21
|
+
require './pinger/commands/service_stop'
|
22
|
+
require './pinger/commands/run_installed_command'
|
20
23
|
|
21
24
|
# metrics
|
22
25
|
require './pinger/metrics/chrome'
|
data/lib/perus/server/app.rb
CHANGED
@@ -197,25 +197,27 @@ module Perus::Server
|
|
197
197
|
|
198
198
|
# create a new action
|
199
199
|
post '/systems/:id/actions' do
|
200
|
-
|
201
|
-
|
200
|
+
Action.add(params['id'], params)
|
201
|
+
redirect "#{url_prefix}systems/#{params['id']}#actions"
|
202
|
+
end
|
202
203
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
204
|
+
# create an action for all systems in a group
|
205
|
+
post '/groups/:id/systems/actions' do
|
206
|
+
group = Group.with_pk!(params['id'])
|
207
|
+
group.systems.each do |system|
|
208
|
+
Action.add(system.id, params)
|
208
209
|
end
|
209
210
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
211
|
+
redirect "#{url_prefix}groups/#{params['id']}/systems"
|
212
|
+
end
|
213
|
+
|
214
|
+
# create an action for all systems
|
215
|
+
post '/systems/actions' do
|
216
|
+
System.each do |system|
|
217
|
+
Action.add(system.id, params)
|
216
218
|
end
|
217
219
|
|
218
|
-
redirect "#{url_prefix}systems
|
220
|
+
redirect "#{url_prefix}systems"
|
219
221
|
end
|
220
222
|
|
221
223
|
# delete an action. deletion also clears any uploaded files.
|
@@ -232,16 +234,7 @@ module Perus::Server
|
|
232
234
|
# overview
|
233
235
|
get '/' do
|
234
236
|
systems = System.all
|
235
|
-
alerts = Alert.all
|
236
|
-
results = alerts.collect do |alert|
|
237
|
-
begin
|
238
|
-
alert.execute(systems)
|
239
|
-
rescue => e
|
240
|
-
"An error occurred running this alert: #{e.inspect}"
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
@alerts = Hash[alerts.zip(results)]
|
237
|
+
@alerts = Alert.all.sort_by(&:severity_level).reverse
|
245
238
|
erb :index
|
246
239
|
end
|
247
240
|
|
@@ -249,6 +242,8 @@ module Perus::Server
|
|
249
242
|
get '/systems' do
|
250
243
|
@systems = System.all.group_by(&:orientation)
|
251
244
|
@title = 'All Systems'
|
245
|
+
@scripts = Script.all
|
246
|
+
@action_url = "systems/actions"
|
252
247
|
erb :systems
|
253
248
|
end
|
254
249
|
|
@@ -257,6 +252,8 @@ module Perus::Server
|
|
257
252
|
group = Group.with_pk!(params['id'])
|
258
253
|
@systems = group.systems.group_by(&:orientation)
|
259
254
|
@title = group.name
|
255
|
+
@scripts = Script.all
|
256
|
+
@action_url = "groups/#{params['id']}/systems/actions"
|
260
257
|
erb :systems
|
261
258
|
end
|
262
259
|
|
@@ -281,7 +278,7 @@ module Perus::Server
|
|
281
278
|
|
282
279
|
# make links clickable
|
283
280
|
@links = @system.links.to_s.gsub("\n", "<br>")
|
284
|
-
URI::extract(@links).each {|uri| @links.gsub!(uri, %Q{<a href="#{uri}">#{uri}</a>})}
|
281
|
+
URI::extract(@links).each {|uri| @links.gsub!(uri, %Q{<a href="#{uri}" target="_new">#{uri}</a>})}
|
285
282
|
|
286
283
|
# last updated is a timestamp, conver
|
287
284
|
if @system.last_updated
|
data/lib/perus/server/db.rb
CHANGED
@@ -18,19 +18,24 @@ module Perus::Server
|
|
18
18
|
Sequel::Migrator.run(@db, File.join(__dir__, 'migrations'))
|
19
19
|
|
20
20
|
# load models - these rely on an existing db connection
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
21
|
+
Dir.chdir(File.join(__dir__, 'models')) do
|
22
|
+
require './system'
|
23
|
+
require './config'
|
24
|
+
require './value'
|
25
|
+
require './group'
|
26
|
+
require './error'
|
27
|
+
require './alert'
|
28
|
+
require './action'
|
29
|
+
require './metric'
|
30
|
+
require './script'
|
31
|
+
require './command_config'
|
32
|
+
require './script_command'
|
33
|
+
require './config_metric'
|
34
|
+
require './active_alert'
|
35
|
+
end
|
36
|
+
end
|
33
37
|
|
38
|
+
def self.start_timers
|
34
39
|
# attempt to run vacuum twice a day. this is done to increase
|
35
40
|
# performance rather than reclaim unused space. as old values and
|
36
41
|
# metrics are deleted the data become very fragmented. vacuuming
|
@@ -55,6 +60,17 @@ module Perus::Server
|
|
55
60
|
# fire every hour
|
56
61
|
cleanup_task.execution_interval = 60 * 60
|
57
62
|
cleanup_task.execute
|
63
|
+
|
64
|
+
# alerts can be process intensive, so to keep page refreshes
|
65
|
+
# responsive the 'active' state of an alert for each system is
|
66
|
+
# cached so lookups can be done against the db, rather than running
|
67
|
+
# each alert for each system on a page load.
|
68
|
+
cache_alerts_task = Concurrent::TimerTask.new do
|
69
|
+
Perus::Server::Alert.cache_active_alerts
|
70
|
+
end
|
71
|
+
|
72
|
+
cache_alerts_task.execution_interval = Server.options.cache_alerts_mins * 60
|
73
|
+
cache_alerts_task.execute
|
58
74
|
end
|
59
75
|
|
60
76
|
def self.cleanup
|
@@ -67,5 +67,25 @@ module Perus::Server
|
|
67
67
|
File.unlink(file_path)
|
68
68
|
end
|
69
69
|
end
|
70
|
+
|
71
|
+
def self.add(system_id, params)
|
72
|
+
action = Action.new
|
73
|
+
action.system_id = system_id
|
74
|
+
|
75
|
+
if params['script_id']
|
76
|
+
action.script_id = params['script_id']
|
77
|
+
else
|
78
|
+
command_config = CommandConfig.create_with_params(params)
|
79
|
+
action.command_config_id = command_config.id
|
80
|
+
end
|
81
|
+
|
82
|
+
begin
|
83
|
+
action.save
|
84
|
+
rescue
|
85
|
+
if action.command_config_id
|
86
|
+
CommandConfig.with_pk!(action.command_config_id).destroy
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
70
90
|
end
|
71
91
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'chronic_duration'
|
2
|
+
|
3
|
+
module Perus::Server
|
4
|
+
class ActiveAlert < Sequel::Model
|
5
|
+
many_to_one :alert
|
6
|
+
many_to_one :system
|
7
|
+
|
8
|
+
def severity
|
9
|
+
alert.severity
|
10
|
+
end
|
11
|
+
|
12
|
+
def active_for
|
13
|
+
ChronicDuration.output(Time.now.to_i - timestamp, format: :short)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.add(alert, system)
|
17
|
+
ActiveAlert.create(
|
18
|
+
system_id: system.id,
|
19
|
+
alert_id: alert.id,
|
20
|
+
timestamp: Time.now.to_i
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,9 +1,68 @@
|
|
1
1
|
module Perus::Server
|
2
2
|
class Alert < Sequel::Model
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
one_to_many :active_alerts
|
4
|
+
|
5
|
+
def execute(system)
|
6
|
+
system.instance_eval(code)
|
7
|
+
end
|
8
|
+
|
9
|
+
def execute_errors
|
10
|
+
values[:errors]
|
11
|
+
end
|
12
|
+
|
13
|
+
def severity_level
|
14
|
+
if active_alerts_dataset.empty?
|
15
|
+
0
|
16
|
+
elsif severity == 'notice'
|
17
|
+
1
|
18
|
+
elsif severity == 'warning'
|
19
|
+
2
|
20
|
+
elsif severity == 'error'
|
21
|
+
3
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def after_destroy
|
26
|
+
super
|
27
|
+
active_alerts.each(&:destroy)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.cache_active_alerts
|
31
|
+
puts 'Caching active alerts'
|
32
|
+
start = Time.now
|
33
|
+
systems = System.all
|
34
|
+
|
35
|
+
Alert.each do |alert|
|
36
|
+
begin
|
37
|
+
# active_alerts are left as is if they are still active
|
38
|
+
currently_active = {}
|
39
|
+
alert.active_alerts.each do |active_alert|
|
40
|
+
currently_active[active_alert.system_id] = active_alert
|
41
|
+
end
|
42
|
+
|
43
|
+
# remove active alerts if they're no longer valid, add new
|
44
|
+
# ones as needed.
|
45
|
+
systems.each do |system|
|
46
|
+
active = alert.execute(system)
|
47
|
+
if active && !currently_active.include?(system.id)
|
48
|
+
ActiveAlert.add(alert, system)
|
49
|
+
elsif !active && currently_active.include?(system.id)
|
50
|
+
currently_active[system.id].destroy
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# no errors occurred by this point, so remove the error
|
55
|
+
# string if it exists from a previous run
|
56
|
+
alert.errors = nil
|
57
|
+
rescue => e
|
58
|
+
alert.errors = e.inspect
|
59
|
+
end
|
60
|
+
|
61
|
+
# update alert.errors
|
62
|
+
alert.save
|
6
63
|
end
|
64
|
+
|
65
|
+
puts "Caching complete, took #{Time.now - start}s"
|
7
66
|
end
|
8
67
|
end
|
9
68
|
end
|
@@ -10,6 +10,7 @@ module Perus::Server
|
|
10
10
|
one_to_many :metrics
|
11
11
|
one_to_many :values
|
12
12
|
one_to_many :actions
|
13
|
+
one_to_many :active_alerts
|
13
14
|
one_to_many :collection_errors, class_name: 'Perus::Server::Error'
|
14
15
|
|
15
16
|
def validate
|
@@ -26,11 +27,20 @@ module Perus::Server
|
|
26
27
|
values.each(&:destroy)
|
27
28
|
actions.each(&:destroy)
|
28
29
|
collection_errors.each(&:destroy)
|
30
|
+
active_alerts.each(&:destroy)
|
29
31
|
|
30
32
|
# remove any uploaded files
|
31
33
|
FileUtils.rm_rf([uploads_dir], secure: true)
|
32
34
|
end
|
33
35
|
|
36
|
+
def alert_class
|
37
|
+
return '' if active_alerts.empty?
|
38
|
+
severities = active_alerts.map(&:severity).uniq
|
39
|
+
return 'error' if severities.include?('error')
|
40
|
+
return 'warning' if severities.include?('warning')
|
41
|
+
return 'notice' if severities.include?('notice')
|
42
|
+
end
|
43
|
+
|
34
44
|
def pending_actions
|
35
45
|
actions_dataset.where(timestamp: nil).all
|
36
46
|
end
|
@@ -78,6 +78,13 @@ main {
|
|
78
78
|
margin-bottom: 30px;
|
79
79
|
}
|
80
80
|
|
81
|
+
main > h1 > input {
|
82
|
+
float: right;
|
83
|
+
width: 180px;
|
84
|
+
height: 22px;
|
85
|
+
margin-top: 2px;
|
86
|
+
}
|
87
|
+
|
81
88
|
main > h2.edit {
|
82
89
|
margin-top: 30px;
|
83
90
|
}
|
@@ -188,11 +195,12 @@ main {
|
|
188
195
|
|
189
196
|
/* systems list */
|
190
197
|
.systems {
|
191
|
-
margin-bottom:
|
198
|
+
margin-bottom: 30px;
|
192
199
|
}
|
193
200
|
|
194
201
|
.systems a {
|
195
202
|
display: inline-block;
|
203
|
+
position: relative;
|
196
204
|
background-color: white;
|
197
205
|
padding: 10px;
|
198
206
|
border-radius: 4px;
|
@@ -224,6 +232,27 @@ main {
|
|
224
232
|
font-weight: 300;
|
225
233
|
}
|
226
234
|
|
235
|
+
.systems a div.alert {
|
236
|
+
width: 12px;
|
237
|
+
height: 12px;
|
238
|
+
border-radius: 12px;
|
239
|
+
position: absolute;
|
240
|
+
bottom: -20px;
|
241
|
+
right: 10px;
|
242
|
+
}
|
243
|
+
|
244
|
+
.systems a div.alert.error {
|
245
|
+
background-color: red;
|
246
|
+
}
|
247
|
+
|
248
|
+
.systems a div.alert.warning {
|
249
|
+
background-color: yellow;
|
250
|
+
}
|
251
|
+
|
252
|
+
.systems a div.alert.notice {
|
253
|
+
background-color: blue;
|
254
|
+
}
|
255
|
+
|
227
256
|
.systems.portrait a {
|
228
257
|
width: 201px;
|
229
258
|
height: 338px;
|
@@ -297,6 +326,10 @@ article.graph {
|
|
297
326
|
margin-bottom: 20px;
|
298
327
|
}
|
299
328
|
|
329
|
+
.dygraph-legend {
|
330
|
+
background-color: rgba(255, 255, 255, 0.5) !important;
|
331
|
+
}
|
332
|
+
|
300
333
|
/* metric collection errors */
|
301
334
|
#errors {
|
302
335
|
padding-top: 20px;
|
@@ -348,39 +381,43 @@ article.graph {
|
|
348
381
|
}
|
349
382
|
|
350
383
|
.alert h1 {
|
351
|
-
margin-bottom:
|
384
|
+
margin-bottom: 5px;
|
352
385
|
}
|
353
386
|
|
354
|
-
.
|
387
|
+
ul.alerts {
|
355
388
|
list-style-type: none;
|
356
389
|
padding: 0px;
|
357
390
|
font-size: 14px;
|
358
391
|
}
|
359
392
|
|
360
|
-
|
361
|
-
|
393
|
+
.alert h1 + ul.alerts {
|
394
|
+
margin-top: 10px;
|
395
|
+
}
|
396
|
+
|
397
|
+
ul.alerts li {
|
398
|
+
padding-left: 20px;
|
362
399
|
position: relative;
|
363
400
|
}
|
364
401
|
|
365
|
-
.
|
402
|
+
ul.alerts li::before {
|
366
403
|
position: absolute;
|
367
404
|
left: 0px;
|
368
405
|
top: 1px;
|
369
406
|
content: ' ';
|
370
|
-
width:
|
371
|
-
height:
|
372
|
-
border-radius:
|
407
|
+
width: 12px;
|
408
|
+
height: 12px;
|
409
|
+
border-radius: 12px;
|
373
410
|
}
|
374
411
|
|
375
|
-
.
|
412
|
+
ul.alerts li.warning::before {
|
376
413
|
background-color: yellow;
|
377
414
|
}
|
378
415
|
|
379
|
-
.
|
416
|
+
ul.alerts li.error::before {
|
380
417
|
background-color: red;
|
381
418
|
}
|
382
419
|
|
383
|
-
.
|
420
|
+
ul.alerts li.notice::before {
|
384
421
|
background-color: blue;
|
385
422
|
}
|
386
423
|
|
@@ -411,6 +448,10 @@ article.graph {
|
|
411
448
|
margin-top: 30px;
|
412
449
|
}
|
413
450
|
|
451
|
+
#no-systems + #add-command {
|
452
|
+
margin-top: 0px;
|
453
|
+
}
|
454
|
+
|
414
455
|
#add-command select {
|
415
456
|
margin-left: 7px;
|
416
457
|
}
|
data/lib/perus/server/server.rb
CHANGED
@@ -9,16 +9,18 @@ DEFAULT_SERVER_OPTIONS = {
|
|
9
9
|
'port' => 3000,
|
10
10
|
'site_name' => 'Perus',
|
11
11
|
'url_prefix' => '/',
|
12
|
-
'keep_hours' => 24
|
12
|
+
'keep_hours' => 24,
|
13
|
+
'cache_alerts_mins' => 1
|
13
14
|
}
|
14
15
|
}
|
15
16
|
|
16
17
|
module Perus::Server
|
17
18
|
class Server
|
18
19
|
def initialize(options_path = DEFAULT_SERVER_OPTIONS_PATH, environment = 'development')
|
19
|
-
self.class.
|
20
|
+
self.class.load_options(options_path)
|
20
21
|
ENV['RACK_ENV'] = environment
|
21
22
|
DB.start
|
23
|
+
DB.start_timers
|
22
24
|
end
|
23
25
|
|
24
26
|
def run
|
@@ -32,5 +34,9 @@ module Perus::Server
|
|
32
34
|
def self.options
|
33
35
|
@options ||= Perus::Options.new
|
34
36
|
end
|
37
|
+
|
38
|
+
def self.load_options(path = DEFAULT_SERVER_OPTIONS_PATH)
|
39
|
+
options.load(path, DEFAULT_SERVER_OPTIONS)
|
40
|
+
end
|
35
41
|
end
|
36
42
|
end
|
@@ -2,19 +2,21 @@
|
|
2
2
|
<% if @alerts.empty? %>
|
3
3
|
<p>No alerts</p>
|
4
4
|
<% else %>
|
5
|
-
<% @alerts.each do |alert
|
6
|
-
<div class="alert
|
5
|
+
<% @alerts.each do |alert| %>
|
6
|
+
<div class="alert">
|
7
7
|
<h1><%= alert.name %></h1>
|
8
|
-
<%
|
9
|
-
<p>
|
10
|
-
<% elsif systems.is_a?(String) %>
|
11
|
-
<p><%= systems.gsub('<', '<').gsub('>', '>') %></p>
|
8
|
+
<% unless alert.execute_errors.nil? || alert.execute_errors.empty? %>
|
9
|
+
<p>An error occurred running this alert: <%= alert.execute_errors.gsub('<', '<').gsub('>', '>') %></p>
|
12
10
|
<% else %>
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
11
|
+
<% if alert.active_alerts.empty? %>
|
12
|
+
<p>All systems functioning normally</p>
|
13
|
+
<% else %>
|
14
|
+
<ul class="alerts">
|
15
|
+
<% alert.active_alerts.each do |active_alert| %>
|
16
|
+
<li class="<%= alert.severity %>"><a href="<%= url_prefix %>systems/<%= active_alert.system.id %>"><%= active_alert.system.name %> (<%= active_alert.active_for %>)</a></li>
|
17
|
+
<% end %>
|
18
|
+
</ul>
|
19
|
+
<% end %>
|
18
20
|
<% end %>
|
19
21
|
</div>
|
20
22
|
<% end %>
|
@@ -10,7 +10,7 @@
|
|
10
10
|
<dt>Last Updated:</dt>
|
11
11
|
<dd><%= @last_updated %></dd>
|
12
12
|
<dt>IP:</dt>
|
13
|
-
<dd><%= @system.ip %></dd>
|
13
|
+
<dd><%= @system.ip || 'unknown IP' %></dd>
|
14
14
|
<% @str_metrics.each do |name, value| %>
|
15
15
|
<dt><%= name %></dt>
|
16
16
|
<dd><%= value %></dd>
|
@@ -19,6 +19,16 @@
|
|
19
19
|
<dt>Links:</dt>
|
20
20
|
<dd><%= @links %></dd>
|
21
21
|
<% end %>
|
22
|
+
<% unless @system.active_alerts.empty? %>
|
23
|
+
<dt>Alerts:</dt>
|
24
|
+
<dd>
|
25
|
+
<ul class="alerts">
|
26
|
+
<% @system.active_alerts.each do |active_alert| %>
|
27
|
+
<li class="<%= active_alert.alert.severity %>"><%= active_alert.alert.name %> (<%= active_alert.active_for %>)</li>
|
28
|
+
<% end %>
|
29
|
+
</ul>
|
30
|
+
</dd>
|
31
|
+
<% end %>
|
22
32
|
</dl>
|
23
33
|
</section>
|
24
34
|
|
@@ -44,7 +54,14 @@
|
|
44
54
|
document.getElementById("metric-<%= name %>"),
|
45
55
|
"<%= url_prefix %>systems/<%= @system.id %>/values?metrics=<%= metrics.join(',') %>",
|
46
56
|
{
|
47
|
-
labelsSeparateLines: true
|
57
|
+
labelsSeparateLines: true,
|
58
|
+
axes: {
|
59
|
+
y: {
|
60
|
+
valueFormatter: function(y) {
|
61
|
+
return y.toFixed(2);
|
62
|
+
}
|
63
|
+
}
|
64
|
+
}
|
48
65
|
}
|
49
66
|
);
|
50
67
|
|
@@ -1,17 +1,86 @@
|
|
1
|
-
<h1
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
1
|
+
<h1>
|
2
|
+
<%= @title %>
|
3
|
+
<input type="text" id="system-search" placeholder="search: name, logical name, ip">
|
4
|
+
</h1>
|
5
|
+
|
6
|
+
<% @systems.each do |(orientation, systems)| %>
|
7
|
+
<div class="<%= orientation %> systems">
|
8
|
+
<% systems.each do |system| %>
|
9
|
+
<a href="<%= url_prefix %>systems/<%= system.id %>" class="system">
|
10
|
+
<div class="screenshot" style="background-image: url(<%= system.screenshot_url %>)"></div>
|
11
|
+
<h1 class="system-id"><%= system.name %></h1>
|
12
|
+
<p><span class="system-id"><%= system.logical_name %></span> - <span class="system-id"><%= system.ip || 'unknown IP' %></span></p>
|
13
|
+
<div class="alert <%= system.alert_class %>"></div>
|
14
|
+
</a>
|
15
|
+
<% end %>
|
16
|
+
</div>
|
17
|
+
<% end %>
|
18
|
+
<p id="no-systems" <% unless @systems.empty? %>style="display: none"<% end %>>No Systems</p>
|
19
|
+
|
20
|
+
<p id="add-command">
|
21
|
+
Add action to <%= @title %>:
|
22
|
+
<select id="command-select">
|
23
|
+
<option></option>
|
24
|
+
<optgroup label="Commands">
|
25
|
+
<% command_actions.each do |command| %>
|
26
|
+
<option value="<%= command.name %>"><%= command.human_name %></option>
|
12
27
|
<% end %>
|
13
|
-
</
|
14
|
-
|
15
|
-
<%
|
16
|
-
|
28
|
+
</optgroup>
|
29
|
+
<optgroup label="Scripts">
|
30
|
+
<% @scripts.each do |script| %>
|
31
|
+
<option value="<%= script.code_name %>"><%= script.name %></option>
|
32
|
+
<% end %>
|
33
|
+
</optgroup>
|
34
|
+
</select>
|
35
|
+
</p>
|
36
|
+
|
37
|
+
<!-- new commands -->
|
38
|
+
<% command_actions.each do |command| %>
|
39
|
+
<%= erb :command_config, locals: {command: command, action: "#{url_prefix}#{@action_url}"} %>
|
40
|
+
<% end %>
|
41
|
+
<% @scripts.each do |script| %>
|
42
|
+
<form class="command" data-command="<%= script.code_name %>" style="display: none" method="POST" action="<%= url_prefix %><%= @action_url %>">
|
43
|
+
<h1><%= script.name %></h1>
|
44
|
+
<p><%= script.description %></p>
|
45
|
+
<input type="hidden" name="script_id" value="<%= script.id %>">
|
46
|
+
<p class="actions"><input type="submit" value="Add"></p>
|
47
|
+
</form>
|
17
48
|
<% end %>
|
49
|
+
|
50
|
+
<script>
|
51
|
+
$('#command-select').change(function() {
|
52
|
+
$('form.command').hide();
|
53
|
+
$('form[data-command="' + $(this).val() + '"]').show();
|
54
|
+
});
|
55
|
+
|
56
|
+
function filter() {
|
57
|
+
var allSystems = $('.system');
|
58
|
+
var anyMatching = false;
|
59
|
+
var query = $.trim($('#system-search').val()).toLowerCase();;
|
60
|
+
|
61
|
+
if (query == '') {
|
62
|
+
allSystems.show();
|
63
|
+
anyMatching = true;
|
64
|
+
|
65
|
+
} else {
|
66
|
+
allSystems.each(function(i, el) {
|
67
|
+
var system = $(el);
|
68
|
+
var ids = system.find('.system-id').text().toLowerCase();
|
69
|
+
if (ids.includes(query)) {
|
70
|
+
anyMatching = true;
|
71
|
+
system.show();
|
72
|
+
} else {
|
73
|
+
system.hide();
|
74
|
+
}
|
75
|
+
});
|
76
|
+
}
|
77
|
+
|
78
|
+
if (anyMatching)
|
79
|
+
$('#no-systems').hide();
|
80
|
+
else
|
81
|
+
$('#no-systems').show();
|
82
|
+
}
|
83
|
+
|
84
|
+
$('#system-search').keyup(filter);
|
85
|
+
$('#system-search').change(filter);
|
86
|
+
</script>
|
data/lib/perus/version.rb
CHANGED
data/lib/perus.rb
CHANGED
@@ -17,9 +17,10 @@ elsif ARGV[0] == 'console'
|
|
17
17
|
# start in the Server namespace
|
18
18
|
include Perus::Server
|
19
19
|
|
20
|
-
# console is used to access the database. initialise
|
21
|
-
#
|
22
|
-
Server.
|
20
|
+
# console is used to access the database. initialise server options to find
|
21
|
+
# the db path and start the database connection.
|
22
|
+
Server.load_options
|
23
|
+
DB.start
|
23
24
|
|
24
25
|
# remove the arg otherwise irb will try to load a file named 'console'
|
25
26
|
ARGV.shift
|
data/perus.gemspec
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.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Will Cannings
|
@@ -178,6 +178,20 @@ dependencies:
|
|
178
178
|
- - "~>"
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: '1.6'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: chronic_duration
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - "~>"
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0.10'
|
188
|
+
type: :runtime
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - "~>"
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0.10'
|
181
195
|
description:
|
182
196
|
email:
|
183
197
|
- me@willcannings.com
|
@@ -210,7 +224,10 @@ files:
|
|
210
224
|
- lib/perus/pinger/commands/remove_path.rb
|
211
225
|
- lib/perus/pinger/commands/replace.rb
|
212
226
|
- lib/perus/pinger/commands/restart.rb
|
227
|
+
- lib/perus/pinger/commands/run_installed_command.rb
|
213
228
|
- lib/perus/pinger/commands/script.rb
|
229
|
+
- lib/perus/pinger/commands/service_start.rb
|
230
|
+
- lib/perus/pinger/commands/service_stop.rb
|
214
231
|
- lib/perus/pinger/commands/sleep.rb
|
215
232
|
- lib/perus/pinger/commands/upgrade.rb
|
216
233
|
- lib/perus/pinger/commands/upload.rb
|
@@ -245,7 +262,10 @@ files:
|
|
245
262
|
- lib/perus/server/migrations/010_create_scripts.rb
|
246
263
|
- lib/perus/server/migrations/011_create_script_commands.rb
|
247
264
|
- lib/perus/server/migrations/012_create_config_metrics.rb
|
265
|
+
- lib/perus/server/migrations/013_create_active_alerts.rb
|
266
|
+
- lib/perus/server/migrations/014_alerts_can_have_errors.rb
|
248
267
|
- lib/perus/server/models/action.rb
|
268
|
+
- lib/perus/server/models/active_alert.rb
|
249
269
|
- lib/perus/server/models/alert.rb
|
250
270
|
- lib/perus/server/models/command_config.rb
|
251
271
|
- lib/perus/server/models/config.rb
|
@@ -327,3 +347,4 @@ signing_key:
|
|
327
347
|
specification_version: 4
|
328
348
|
summary: Simple system overview server
|
329
349
|
test_files: []
|
350
|
+
has_rdoc:
|