perus 0.1.7 → 0.1.8
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/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:
|