perus 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +4 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +16 -0
  8. data/Rakefile +6 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +7 -0
  11. data/exe/perus-pinger +23 -0
  12. data/exe/perus-server +23 -0
  13. data/lib/perus/options.rb +30 -0
  14. data/lib/perus/pinger/chrome_command.rb +54 -0
  15. data/lib/perus/pinger/command.rb +123 -0
  16. data/lib/perus/pinger/commands/chrome_execute.rb +26 -0
  17. data/lib/perus/pinger/commands/chrome_navigate.rb +25 -0
  18. data/lib/perus/pinger/commands/chrome_reload.rb +29 -0
  19. data/lib/perus/pinger/commands/kill_process.rb +13 -0
  20. data/lib/perus/pinger/commands/remove_path.rb +15 -0
  21. data/lib/perus/pinger/commands/replace.rb +18 -0
  22. data/lib/perus/pinger/commands/restart.rb +9 -0
  23. data/lib/perus/pinger/commands/script.rb +59 -0
  24. data/lib/perus/pinger/commands/upgrade.rb +16 -0
  25. data/lib/perus/pinger/commands/upload.rb +15 -0
  26. data/lib/perus/pinger/metrics/chrome.rb +33 -0
  27. data/lib/perus/pinger/metrics/cpu.rb +17 -0
  28. data/lib/perus/pinger/metrics/hd.rb +14 -0
  29. data/lib/perus/pinger/metrics/mem.rb +11 -0
  30. data/lib/perus/pinger/metrics/process.rb +27 -0
  31. data/lib/perus/pinger/metrics/screenshot.rb +36 -0
  32. data/lib/perus/pinger/metrics/temp.rb +18 -0
  33. data/lib/perus/pinger/metrics/uptime.rb +10 -0
  34. data/lib/perus/pinger/metrics/value.rb +18 -0
  35. data/lib/perus/pinger/pinger.rb +218 -0
  36. data/lib/perus/pinger.rb +38 -0
  37. data/lib/perus/server/admin.rb +83 -0
  38. data/lib/perus/server/app.rb +304 -0
  39. data/lib/perus/server/db.rb +29 -0
  40. data/lib/perus/server/form.rb +73 -0
  41. data/lib/perus/server/helpers.rb +36 -0
  42. data/lib/perus/server/migrations/001_create_systems.rb +20 -0
  43. data/lib/perus/server/migrations/002_create_configs.rb +12 -0
  44. data/lib/perus/server/migrations/003_create_values.rb +23 -0
  45. data/lib/perus/server/migrations/004_create_groups.rb +12 -0
  46. data/lib/perus/server/migrations/005_create_errors.rb +15 -0
  47. data/lib/perus/server/migrations/006_create_alerts.rb +14 -0
  48. data/lib/perus/server/migrations/007_create_actions.rb +18 -0
  49. data/lib/perus/server/migrations/008_create_metrics.rb +16 -0
  50. data/lib/perus/server/migrations/009_create_command_config.rb +13 -0
  51. data/lib/perus/server/migrations/010_create_scripts.rb +13 -0
  52. data/lib/perus/server/migrations/011_create_script_commands.rb +14 -0
  53. data/lib/perus/server/migrations/012_create_config_metrics.rb +14 -0
  54. data/lib/perus/server/models/action.rb +71 -0
  55. data/lib/perus/server/models/alert.rb +9 -0
  56. data/lib/perus/server/models/command_config.rb +58 -0
  57. data/lib/perus/server/models/config.rb +30 -0
  58. data/lib/perus/server/models/config_metric.rb +15 -0
  59. data/lib/perus/server/models/error.rb +5 -0
  60. data/lib/perus/server/models/group.rb +12 -0
  61. data/lib/perus/server/models/metric.rb +53 -0
  62. data/lib/perus/server/models/script.rb +39 -0
  63. data/lib/perus/server/models/script_command.rb +15 -0
  64. data/lib/perus/server/models/system.rb +160 -0
  65. data/lib/perus/server/models/value.rb +11 -0
  66. data/lib/perus/server/public/css/reset.css +102 -0
  67. data/lib/perus/server/public/css/style.css +461 -0
  68. data/lib/perus/server/public/fonts/opensans/OpenSans-Italic.ttf +0 -0
  69. data/lib/perus/server/public/fonts/opensans/OpenSans-Light.ttf +0 -0
  70. data/lib/perus/server/public/fonts/opensans/OpenSans-LightItalic.ttf +0 -0
  71. data/lib/perus/server/public/fonts/opensans/OpenSans-Regular.ttf +0 -0
  72. data/lib/perus/server/public/fonts/opensans/OpenSans-Semibold.ttf +0 -0
  73. data/lib/perus/server/public/fonts/opensans/OpenSans-SemiboldItalic.ttf +0 -0
  74. data/lib/perus/server/public/fonts/opensans/opensans-light-webfont.eot +0 -0
  75. data/lib/perus/server/public/fonts/opensans/opensans-light-webfont.svg +1824 -0
  76. data/lib/perus/server/public/fonts/opensans/opensans-light-webfont.ttf +0 -0
  77. data/lib/perus/server/public/fonts/opensans/opensans-light-webfont.woff +0 -0
  78. data/lib/perus/server/public/fonts/opensans/opensans-light-webfont.woff2 +0 -0
  79. data/lib/perus/server/public/fonts/opensans/opensans-regular-webfont.eot +0 -0
  80. data/lib/perus/server/public/fonts/opensans/opensans-regular-webfont.svg +1824 -0
  81. data/lib/perus/server/public/fonts/opensans/opensans-regular-webfont.ttf +0 -0
  82. data/lib/perus/server/public/fonts/opensans/opensans-regular-webfont.woff +0 -0
  83. data/lib/perus/server/public/fonts/opensans/opensans-regular-webfont.woff2 +0 -0
  84. data/lib/perus/server/public/fonts/opensans/opensans-semibold-webfont.eot +0 -0
  85. data/lib/perus/server/public/fonts/opensans/opensans-semibold-webfont.svg +1824 -0
  86. data/lib/perus/server/public/fonts/opensans/opensans-semibold-webfont.ttf +0 -0
  87. data/lib/perus/server/public/fonts/opensans/opensans-semibold-webfont.woff +0 -0
  88. data/lib/perus/server/public/fonts/opensans/opensans-semibold-webfont.woff2 +0 -0
  89. data/lib/perus/server/public/fonts/opensans/stylesheet.css +35 -0
  90. data/lib/perus/server/public/js/dygraph-combined.js +6 -0
  91. data/lib/perus/server/public/js/jquery.js +4 -0
  92. data/lib/perus/server/server.rb +32 -0
  93. data/lib/perus/server/views/admin/edit.erb +9 -0
  94. data/lib/perus/server/views/admin/index.erb +18 -0
  95. data/lib/perus/server/views/admin/new.erb +5 -0
  96. data/lib/perus/server/views/alerts/form.erb +3 -0
  97. data/lib/perus/server/views/command_config.erb +37 -0
  98. data/lib/perus/server/views/configs/edit.erb +36 -0
  99. data/lib/perus/server/views/configs/form.erb +1 -0
  100. data/lib/perus/server/views/errors.erb +6 -0
  101. data/lib/perus/server/views/groups/form.erb +1 -0
  102. data/lib/perus/server/views/index.erb +21 -0
  103. data/lib/perus/server/views/layout.erb +44 -0
  104. data/lib/perus/server/views/scripts/edit.erb +36 -0
  105. data/lib/perus/server/views/scripts/form.erb +2 -0
  106. data/lib/perus/server/views/system.erb +202 -0
  107. data/lib/perus/server/views/systems/form.erb +6 -0
  108. data/lib/perus/server/views/systems.erb +17 -0
  109. data/lib/perus/server.rb +24 -0
  110. data/lib/perus/version.rb +3 -0
  111. data/lib/perus.rb +10 -0
  112. data/perus.gemspec +36 -0
  113. 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
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .DS_Store
11
+ *.db
12
+ uploads
13
+
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
4
+ before_install: gem install bundler -v 1.10.4
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in perus.gemspec
4
+ gemspec
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
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
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
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
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,9 @@
1
+ module Perus::Pinger
2
+ class Restart < Command
3
+ description 'Restarts the client computer'
4
+
5
+ def run
6
+ -> { `sudo reboot` }
7
+ end
8
+ end
9
+ 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