perus 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +16 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/exe/perus-pinger +23 -0
- data/exe/perus-server +23 -0
- data/lib/perus/options.rb +30 -0
- data/lib/perus/pinger/chrome_command.rb +54 -0
- data/lib/perus/pinger/command.rb +123 -0
- data/lib/perus/pinger/commands/chrome_execute.rb +26 -0
- data/lib/perus/pinger/commands/chrome_navigate.rb +25 -0
- data/lib/perus/pinger/commands/chrome_reload.rb +29 -0
- data/lib/perus/pinger/commands/kill_process.rb +13 -0
- data/lib/perus/pinger/commands/remove_path.rb +15 -0
- data/lib/perus/pinger/commands/replace.rb +18 -0
- data/lib/perus/pinger/commands/restart.rb +9 -0
- data/lib/perus/pinger/commands/script.rb +59 -0
- data/lib/perus/pinger/commands/upgrade.rb +16 -0
- data/lib/perus/pinger/commands/upload.rb +15 -0
- data/lib/perus/pinger/metrics/chrome.rb +33 -0
- data/lib/perus/pinger/metrics/cpu.rb +17 -0
- data/lib/perus/pinger/metrics/hd.rb +14 -0
- data/lib/perus/pinger/metrics/mem.rb +11 -0
- data/lib/perus/pinger/metrics/process.rb +27 -0
- data/lib/perus/pinger/metrics/screenshot.rb +36 -0
- data/lib/perus/pinger/metrics/temp.rb +18 -0
- data/lib/perus/pinger/metrics/uptime.rb +10 -0
- data/lib/perus/pinger/metrics/value.rb +18 -0
- data/lib/perus/pinger/pinger.rb +218 -0
- data/lib/perus/pinger.rb +38 -0
- data/lib/perus/server/admin.rb +83 -0
- data/lib/perus/server/app.rb +304 -0
- data/lib/perus/server/db.rb +29 -0
- data/lib/perus/server/form.rb +73 -0
- data/lib/perus/server/helpers.rb +36 -0
- data/lib/perus/server/migrations/001_create_systems.rb +20 -0
- data/lib/perus/server/migrations/002_create_configs.rb +12 -0
- data/lib/perus/server/migrations/003_create_values.rb +23 -0
- data/lib/perus/server/migrations/004_create_groups.rb +12 -0
- data/lib/perus/server/migrations/005_create_errors.rb +15 -0
- data/lib/perus/server/migrations/006_create_alerts.rb +14 -0
- data/lib/perus/server/migrations/007_create_actions.rb +18 -0
- data/lib/perus/server/migrations/008_create_metrics.rb +16 -0
- data/lib/perus/server/migrations/009_create_command_config.rb +13 -0
- data/lib/perus/server/migrations/010_create_scripts.rb +13 -0
- data/lib/perus/server/migrations/011_create_script_commands.rb +14 -0
- data/lib/perus/server/migrations/012_create_config_metrics.rb +14 -0
- data/lib/perus/server/models/action.rb +71 -0
- data/lib/perus/server/models/alert.rb +9 -0
- data/lib/perus/server/models/command_config.rb +58 -0
- data/lib/perus/server/models/config.rb +30 -0
- data/lib/perus/server/models/config_metric.rb +15 -0
- data/lib/perus/server/models/error.rb +5 -0
- data/lib/perus/server/models/group.rb +12 -0
- data/lib/perus/server/models/metric.rb +53 -0
- data/lib/perus/server/models/script.rb +39 -0
- data/lib/perus/server/models/script_command.rb +15 -0
- data/lib/perus/server/models/system.rb +160 -0
- data/lib/perus/server/models/value.rb +11 -0
- data/lib/perus/server/public/css/reset.css +102 -0
- data/lib/perus/server/public/css/style.css +461 -0
- data/lib/perus/server/public/fonts/opensans/OpenSans-Italic.ttf +0 -0
- data/lib/perus/server/public/fonts/opensans/OpenSans-Light.ttf +0 -0
- data/lib/perus/server/public/fonts/opensans/OpenSans-LightItalic.ttf +0 -0
- data/lib/perus/server/public/fonts/opensans/OpenSans-Regular.ttf +0 -0
- data/lib/perus/server/public/fonts/opensans/OpenSans-Semibold.ttf +0 -0
- data/lib/perus/server/public/fonts/opensans/OpenSans-SemiboldItalic.ttf +0 -0
- data/lib/perus/server/public/fonts/opensans/opensans-light-webfont.eot +0 -0
- data/lib/perus/server/public/fonts/opensans/opensans-light-webfont.svg +1824 -0
- data/lib/perus/server/public/fonts/opensans/opensans-light-webfont.ttf +0 -0
- data/lib/perus/server/public/fonts/opensans/opensans-light-webfont.woff +0 -0
- data/lib/perus/server/public/fonts/opensans/opensans-light-webfont.woff2 +0 -0
- data/lib/perus/server/public/fonts/opensans/opensans-regular-webfont.eot +0 -0
- data/lib/perus/server/public/fonts/opensans/opensans-regular-webfont.svg +1824 -0
- data/lib/perus/server/public/fonts/opensans/opensans-regular-webfont.ttf +0 -0
- data/lib/perus/server/public/fonts/opensans/opensans-regular-webfont.woff +0 -0
- data/lib/perus/server/public/fonts/opensans/opensans-regular-webfont.woff2 +0 -0
- data/lib/perus/server/public/fonts/opensans/opensans-semibold-webfont.eot +0 -0
- data/lib/perus/server/public/fonts/opensans/opensans-semibold-webfont.svg +1824 -0
- data/lib/perus/server/public/fonts/opensans/opensans-semibold-webfont.ttf +0 -0
- data/lib/perus/server/public/fonts/opensans/opensans-semibold-webfont.woff +0 -0
- data/lib/perus/server/public/fonts/opensans/opensans-semibold-webfont.woff2 +0 -0
- data/lib/perus/server/public/fonts/opensans/stylesheet.css +35 -0
- data/lib/perus/server/public/js/dygraph-combined.js +6 -0
- data/lib/perus/server/public/js/jquery.js +4 -0
- data/lib/perus/server/server.rb +32 -0
- data/lib/perus/server/views/admin/edit.erb +9 -0
- data/lib/perus/server/views/admin/index.erb +18 -0
- data/lib/perus/server/views/admin/new.erb +5 -0
- data/lib/perus/server/views/alerts/form.erb +3 -0
- data/lib/perus/server/views/command_config.erb +37 -0
- data/lib/perus/server/views/configs/edit.erb +36 -0
- data/lib/perus/server/views/configs/form.erb +1 -0
- data/lib/perus/server/views/errors.erb +6 -0
- data/lib/perus/server/views/groups/form.erb +1 -0
- data/lib/perus/server/views/index.erb +21 -0
- data/lib/perus/server/views/layout.erb +44 -0
- data/lib/perus/server/views/scripts/edit.erb +36 -0
- data/lib/perus/server/views/scripts/form.erb +2 -0
- data/lib/perus/server/views/system.erb +202 -0
- data/lib/perus/server/views/systems/form.erb +6 -0
- data/lib/perus/server/views/systems.erb +17 -0
- data/lib/perus/server.rb +24 -0
- data/lib/perus/version.rb +3 -0
- data/lib/perus.rb +10 -0
- data/perus.gemspec +36 -0
- metadata +354 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 50163f19be6adf3f481d868e88b7efe33d563425
|
4
|
+
data.tar.gz: 46fbde86dcfb9d0f6194849517f9b6bae81ec8cd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 504e5ec4a3c322c18de258f2ee67eea50267bb82aff92473eca31b2205091b4058e16f1f3ef17a8d2581b536d0bf99fadd8b7eae87c3b6b0632a3f2aec87404a
|
7
|
+
data.tar.gz: faa25da90c07daac501192daac00106c568ab73e177fa93578de5116abbb333f50697706d31e2fbf8ded7327c8709f4d0e38f5f656a06abcaa73b45980ba036b
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 ACA
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# Perus
|
2
|
+
|
3
|
+
A simple system management and monitoring service. Systems ping a server with screenshots and data, and the server provides a simple overview page.
|
4
|
+
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Install on a server and each system you'd like to monitor
|
9
|
+
|
10
|
+
$ gem install perus
|
11
|
+
|
12
|
+
|
13
|
+
## License
|
14
|
+
|
15
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
16
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "perus"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/exe/perus-pinger
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'perus'
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
options_path = Perus::Pinger::DEFAULT_PINGER_OPTIONS_PATH
|
6
|
+
|
7
|
+
ARGV.options do |opts|
|
8
|
+
opts.banner = "Usage: perus-pinger [options]"
|
9
|
+
|
10
|
+
opts.on('-c', '--config', String, "Path to config file (default: #{Perus::Pinger::DEFAULT_PINGER_OPTIONS_PATH})") do |c|
|
11
|
+
options_path = c
|
12
|
+
end
|
13
|
+
|
14
|
+
opts.on('-h', '--help', 'Prints this help') do
|
15
|
+
puts opts
|
16
|
+
exit
|
17
|
+
end
|
18
|
+
|
19
|
+
opts.parse!
|
20
|
+
end
|
21
|
+
|
22
|
+
pinger = Perus::Pinger::Pinger.new(options_path)
|
23
|
+
pinger.run
|
data/exe/perus-server
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'perus'
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
options_path = Perus::Server::DEFAULT_SERVER_OPTIONS_PATH
|
6
|
+
|
7
|
+
ARGV.options do |opts|
|
8
|
+
opts.banner = "Usage: perus-server [options]"
|
9
|
+
|
10
|
+
opts.on('-c', '--config', String, "Path to config file (default: #{Perus::Server::DEFAULT_SERVER_OPTIONS_PATH})") do |c|
|
11
|
+
options_path = c
|
12
|
+
end
|
13
|
+
|
14
|
+
opts.on('-h', '--help', 'Prints this help') do
|
15
|
+
puts opts
|
16
|
+
exit
|
17
|
+
end
|
18
|
+
|
19
|
+
opts.parse!
|
20
|
+
end
|
21
|
+
|
22
|
+
server = Perus::Server::Server.new(options_path)
|
23
|
+
server.run
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'iniparse'
|
2
|
+
|
3
|
+
module Perus
|
4
|
+
class Options
|
5
|
+
def initialize()
|
6
|
+
@defaults = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def load(path, defaults)
|
10
|
+
if File.exists?(path)
|
11
|
+
user_options = IniParse.parse(IO.read(path)).to_h
|
12
|
+
else
|
13
|
+
user_options = {}
|
14
|
+
end
|
15
|
+
@options = defaults.merge(user_options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def method_missing(name, *params, &block)
|
19
|
+
if @options.include?(name.to_s)
|
20
|
+
@options[name.to_s]
|
21
|
+
else
|
22
|
+
@options['__anonymous__'][name.to_s]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def [](name)
|
27
|
+
@options[name]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'faye/websocket'
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'rest-client'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module Perus::Pinger
|
7
|
+
class ChromeCommand < Command
|
8
|
+
option :timeout_seconds, default: 2
|
9
|
+
option :host, default: 'localhost'
|
10
|
+
option :port, default: 9222
|
11
|
+
abstract!
|
12
|
+
|
13
|
+
def send_command(command)
|
14
|
+
@ws.send(command)
|
15
|
+
end
|
16
|
+
|
17
|
+
def execute(commands, &message_callback)
|
18
|
+
# discover the first page shown in chrome
|
19
|
+
pages = JSON.parse(RestClient.get("http://#{options.host}:#{options.port}/json"))
|
20
|
+
pages.reject! {|page| page['url'].include?('chrome-extension')}
|
21
|
+
@page = pages.first
|
22
|
+
|
23
|
+
EM.run do
|
24
|
+
@ws = Faye::WebSocket::Client.new(@page['webSocketDebuggerUrl'])
|
25
|
+
|
26
|
+
@ws.on :error do |event|
|
27
|
+
puts "Chrome error: #{event}"
|
28
|
+
EM.stop_event_loop
|
29
|
+
end
|
30
|
+
|
31
|
+
@ws.on :close do |event|
|
32
|
+
EM.stop_event_loop
|
33
|
+
end
|
34
|
+
|
35
|
+
@ws.on :message do |event|
|
36
|
+
if block_given?
|
37
|
+
json = JSON.parse(event.data)
|
38
|
+
message_callback.call(json)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# send each command, responses will appear
|
43
|
+
commands.each do |command|
|
44
|
+
send_command(command)
|
45
|
+
end
|
46
|
+
|
47
|
+
# cutoff console message loading after N seconds
|
48
|
+
EM.add_timer(options.timeout_seconds) do
|
49
|
+
@ws.close
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Perus::Pinger
|
4
|
+
class Option
|
5
|
+
attr_reader :name, :default, :restricted
|
6
|
+
|
7
|
+
def initialize(name, settings, command)
|
8
|
+
@name = name
|
9
|
+
@command = command
|
10
|
+
@default = settings[:default]
|
11
|
+
@restricted = settings[:restricted] == true
|
12
|
+
end
|
13
|
+
|
14
|
+
def boolean?
|
15
|
+
default.is_a?(TrueClass) || default.is_a?(FalseClass)
|
16
|
+
end
|
17
|
+
|
18
|
+
def process(results, values)
|
19
|
+
value = values[name.to_s]
|
20
|
+
value = value || default
|
21
|
+
|
22
|
+
if value.nil?
|
23
|
+
raise "#{name} is a required option"
|
24
|
+
end
|
25
|
+
|
26
|
+
if restricted
|
27
|
+
allowed = Pinger.options[@command.name.demodulize][@name.to_s]
|
28
|
+
raise "the value passed to #{@name} is not allowed" unless allowed.include?(value)
|
29
|
+
end
|
30
|
+
|
31
|
+
results[name] = value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Command
|
36
|
+
attr_reader :options, :id
|
37
|
+
|
38
|
+
def self.human_name
|
39
|
+
name.demodulize.underscore.humanize.titlecase
|
40
|
+
end
|
41
|
+
|
42
|
+
# set or get command/metric description
|
43
|
+
def self.description(text = nil)
|
44
|
+
if text
|
45
|
+
@description = text
|
46
|
+
else
|
47
|
+
@description
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# add an option to the command/metric. both the class and instances
|
52
|
+
# of the class have an options method. the class version returns a
|
53
|
+
# list of Option objects representing possible options for the
|
54
|
+
# command. the object version returns an OpenStruct hash of options
|
55
|
+
# and their values (defaults merged with provided values)
|
56
|
+
def self.option(name, option_settings = {})
|
57
|
+
@options ||= []
|
58
|
+
@options << Option.new(name, option_settings, self)
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.options
|
62
|
+
@options ||= []
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.inherited(subclass)
|
66
|
+
subclass.options.concat(self.options)
|
67
|
+
Command.subclasses << subclass
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.subclasses
|
71
|
+
@subclasses ||= []
|
72
|
+
end
|
73
|
+
|
74
|
+
# command classes which are intended to run as metrics call this method
|
75
|
+
def self.metric!
|
76
|
+
@metric = true
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.metric?
|
80
|
+
@metric
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.abstract!
|
84
|
+
@abstract = true
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.abstract?
|
88
|
+
@abstract
|
89
|
+
end
|
90
|
+
|
91
|
+
# create a command instance and initialise it with the provided option
|
92
|
+
# values (hash where keys are option names). any restricted options are
|
93
|
+
# validated first, and an exception is thrown if the provided value is
|
94
|
+
# not one of the allowed values for the option.
|
95
|
+
def initialize(option_values, id = nil)
|
96
|
+
@options = OpenStruct.new
|
97
|
+
self.class.options.each do |option|
|
98
|
+
option.process(@options, option_values)
|
99
|
+
end
|
100
|
+
|
101
|
+
# commands (not metrics) have ids that uniquely identify a command
|
102
|
+
# instance and its response
|
103
|
+
@id = id
|
104
|
+
end
|
105
|
+
|
106
|
+
def run
|
107
|
+
# run command/metric, return types for a command are:
|
108
|
+
# true: successful (will show as success on the server)
|
109
|
+
# string: failed, string should provide the reason
|
110
|
+
# file: file to upload
|
111
|
+
# proc: code to run after returning success to server
|
112
|
+
# hash: metrics are expected to only return a hash with
|
113
|
+
# keys indicating metric names, and values restricted to
|
114
|
+
# files, numerics and strings.
|
115
|
+
# exceptions are caught and shown as errors on the server.
|
116
|
+
end
|
117
|
+
|
118
|
+
def cleanup
|
119
|
+
# called after sending data to server. remove temporary
|
120
|
+
# files etc.
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Perus::Pinger
|
2
|
+
class ChromeExecute < ChromeCommand
|
3
|
+
description 'Executes JavaScript in the top Chrome window. The result
|
4
|
+
of the execution is stored and sent to the server.'
|
5
|
+
option :js
|
6
|
+
|
7
|
+
def run
|
8
|
+
result = false
|
9
|
+
command = '{"id":1,"method":"Runtime.evaluate","params":{"expression":"' + options.js.gsub('"', '\\"') + '","objectGroup":"perus","returnByValue":true}}'
|
10
|
+
|
11
|
+
execute([command]) do |message|
|
12
|
+
if message.include?('id') && message['id'] == 1
|
13
|
+
if message.include?('result')
|
14
|
+
result = message['result'].to_s
|
15
|
+
elsif message.include?('error')
|
16
|
+
result = message['error'].to_s
|
17
|
+
else
|
18
|
+
result = false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
result
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Perus::Pinger
|
2
|
+
class ChromeNavigate < ChromeCommand
|
3
|
+
description 'Changes the URL of the top Chrome window to "url"'
|
4
|
+
option :url
|
5
|
+
|
6
|
+
def run
|
7
|
+
result = false
|
8
|
+
command = '{"id":1,"method":"Page.navigate","params":{"url":"' + options.url + '"}}'
|
9
|
+
|
10
|
+
execute([command]) do |message|
|
11
|
+
if message.include?('id') && message['id'] == 1
|
12
|
+
if message.include?('result')
|
13
|
+
result = true
|
14
|
+
elsif message.include?('error')
|
15
|
+
result = message['error'].to_s
|
16
|
+
else
|
17
|
+
result = message.to_s
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
result
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Perus::Pinger
|
2
|
+
class ChromeReload < ChromeCommand
|
3
|
+
description 'Reloads the top Chrome window. When "ignore_cache" is true
|
4
|
+
the effect is equivalent to performing a force reload.'
|
5
|
+
option :ignore_cache, default: false
|
6
|
+
|
7
|
+
def run
|
8
|
+
result = false
|
9
|
+
|
10
|
+
execute(['{"id":1,"method":"Page.reload"}']) do |message|
|
11
|
+
if message.include?('id') && message['id'] == 1
|
12
|
+
if message.include?('result')
|
13
|
+
if message['result'] == {}
|
14
|
+
result = true
|
15
|
+
else
|
16
|
+
result = message['result'].to_s
|
17
|
+
end
|
18
|
+
elsif message.include?('error')
|
19
|
+
result = message['error'].to_s
|
20
|
+
else
|
21
|
+
result = message.to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
result
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Perus::Pinger
|
2
|
+
class KillProcess < Command
|
3
|
+
description 'Kills all instances of a process. Valid values for
|
4
|
+
"process_name" are contained in the pinger config file.'
|
5
|
+
option :process_name, restricted: true
|
6
|
+
option :signal, default: 'KILL'
|
7
|
+
|
8
|
+
def run
|
9
|
+
result = `killall -#{option.signal} #{option.process_name}`
|
10
|
+
result.include?('no process found') ? result : true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require'fileutils'
|
2
|
+
|
3
|
+
module Perus::Pinger
|
4
|
+
class RemovePath < Command
|
5
|
+
description 'Deletes a file or folder. If "path" is a folder, all files
|
6
|
+
and folders within the folder are deleted as well. Valid
|
7
|
+
values for "path" are contained in the pinger config file.'
|
8
|
+
option :path, restricted: true
|
9
|
+
|
10
|
+
def run
|
11
|
+
FileUtils.rm_rf([File.expand_path(options.path)], secure: true)
|
12
|
+
true # with no exceptions, the removal was successful
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Perus::Pinger
|
2
|
+
class Replace < Command
|
3
|
+
description 'Looks for the string matched by "grep" in the file
|
4
|
+
specified by "path" and replaces it with "replacement".
|
5
|
+
Valid values for "path" are contained in the pinger
|
6
|
+
config file.'
|
7
|
+
option :path, restricted: true
|
8
|
+
option :grep
|
9
|
+
option :replacement
|
10
|
+
|
11
|
+
def run
|
12
|
+
text = IO.read(options.path)
|
13
|
+
text.gsub!(/#{options.grep}/, options.replacement)
|
14
|
+
IO.write(options.path, text)
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Perus::Pinger
|
2
|
+
class Script < Command
|
3
|
+
option :commands
|
4
|
+
abstract!
|
5
|
+
|
6
|
+
def run
|
7
|
+
actions = []
|
8
|
+
results = []
|
9
|
+
late_actions = []
|
10
|
+
|
11
|
+
options.commands.each do |config|
|
12
|
+
begin
|
13
|
+
command = ::Perus::Pinger.const_get(config['type'])
|
14
|
+
actions << command.new(config['options'], config['id'])
|
15
|
+
rescue => e
|
16
|
+
if config['id']
|
17
|
+
results[config['id']] = e.inspect
|
18
|
+
else
|
19
|
+
puts 'Error - action does not have an associated id'
|
20
|
+
p config
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
actions.each do |action|
|
26
|
+
begin
|
27
|
+
result = action.run
|
28
|
+
|
29
|
+
if result.instance_of?(Proc)
|
30
|
+
late_actions << result
|
31
|
+
result = true
|
32
|
+
end
|
33
|
+
|
34
|
+
results << result
|
35
|
+
rescue => e
|
36
|
+
results << e.inspect
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
failed = results.any? {|result| result.is_a?(String)}
|
41
|
+
return results.join(', ') if failed
|
42
|
+
|
43
|
+
if late_actions.empty?
|
44
|
+
true
|
45
|
+
else
|
46
|
+
Proc.new do
|
47
|
+
late_actions.each do |code|
|
48
|
+
begin
|
49
|
+
code.call
|
50
|
+
rescue => e
|
51
|
+
puts 'Error running late action'
|
52
|
+
puts e.inspect
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Perus::Pinger
|
2
|
+
class Upgrade < Command
|
3
|
+
description 'Upgrades Perus on the client machine'
|
4
|
+
option :sudo, default: false
|
5
|
+
|
6
|
+
def run
|
7
|
+
if options.sudo
|
8
|
+
result = `sudo gem upgrade perus`
|
9
|
+
else
|
10
|
+
result = `gem upgrade perus`
|
11
|
+
end
|
12
|
+
|
13
|
+
result.include?('ERROR') ? result : true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Perus::Pinger
|
2
|
+
class Upload < Command
|
3
|
+
description 'Uploads a file from the client to the server. Valid values
|
4
|
+
for "path" are contained in the pinger config file.'
|
5
|
+
option :path, restricted: true
|
6
|
+
|
7
|
+
def run
|
8
|
+
@file = File.new(options.path)
|
9
|
+
end
|
10
|
+
|
11
|
+
def cleanup
|
12
|
+
@file.close unless @file.nil? || @file.closed?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Perus::Pinger
|
2
|
+
class Chrome < ChromeCommand
|
3
|
+
description 'Connects to Chrome with the remote debugger and counts the
|
4
|
+
number of warnings and errors currently in the console of
|
5
|
+
the top Chrome window. The URL of the page is also sent
|
6
|
+
and can be compared to an expected string in an alert.'
|
7
|
+
metric!
|
8
|
+
|
9
|
+
def run
|
10
|
+
# we use a debugging protocol connection to read the console messages
|
11
|
+
# in the top level window to count the number of warnings and errors
|
12
|
+
warning_count = 0
|
13
|
+
error_count = 0
|
14
|
+
|
15
|
+
execute(['{"id":1,"method":"Console.enable"}']) do |json|
|
16
|
+
if json['method'] == 'Console.messageAdded'
|
17
|
+
level = json['params']['message']['level']
|
18
|
+
if level == 'error'
|
19
|
+
error_count += 1
|
20
|
+
elsif level == 'warning'
|
21
|
+
warning_count += 1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
{
|
27
|
+
chrome_warnings: warning_count,
|
28
|
+
chrome_errors: error_count,
|
29
|
+
chrome_url: @page['url']
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Perus::Pinger
|
2
|
+
class CPU < Command
|
3
|
+
description 'Measures overall system CPU usage as a percentage on the
|
4
|
+
client.'
|
5
|
+
metric!
|
6
|
+
|
7
|
+
def run
|
8
|
+
if `uname -s`.strip == 'Darwin'
|
9
|
+
percent = 100 - `iostat -n 0`.split("\n")[2].split[2].to_i
|
10
|
+
else
|
11
|
+
percent = `grep 'cpu ' /proc/stat | awk '{print (1 - ($5 / ($2+$3+$4+$5+$6+$7+$8)))*100}'`
|
12
|
+
end
|
13
|
+
|
14
|
+
{cpu_all: percent.to_f}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Perus::Pinger
|
2
|
+
class HD < Command
|
3
|
+
description 'Measures the percentage of disk space currently used on
|
4
|
+
the specified drive.'
|
5
|
+
option :drive
|
6
|
+
metric!
|
7
|
+
|
8
|
+
def run
|
9
|
+
regex = "/^#{options.drive.gsub("/", "\\/")}/"
|
10
|
+
percent = `df -h / | awk '#{regex} {print $5}'`
|
11
|
+
{hd_used: percent.to_i}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|