hive-runner 2.0.0
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 +7 -0
- data/README.md +120 -0
- data/bin/hive_setup +182 -0
- data/bin/hived +35 -0
- data/bin/start_hive +120 -0
- data/lib/hive/controller/shell.rb +23 -0
- data/lib/hive/controller.rb +32 -0
- data/lib/hive/device/shell.rb +14 -0
- data/lib/hive/device.rb +88 -0
- data/lib/hive/diagnostic.rb +54 -0
- data/lib/hive/diagnostic_runner.rb +32 -0
- data/lib/hive/execution_script.rb +121 -0
- data/lib/hive/file_system.rb +103 -0
- data/lib/hive/log.rb +60 -0
- data/lib/hive/port_allocator.rb +79 -0
- data/lib/hive/register.rb +120 -0
- data/lib/hive/results.rb +20 -0
- data/lib/hive/worker/shell.rb +16 -0
- data/lib/hive/worker.rb +400 -0
- data/lib/hive.rb +118 -0
- data/scripts/hive-script-helper.sh +17 -0
- metadata +262 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0a9df8820b8d030fa96b76d253723ccae821feb1
|
4
|
+
data.tar.gz: 7c2b1f9e1f75d269652e7e996ca515a135b92349
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9e1d1ebb42336cc59d7f5f37fe98392887cadce6c23ef9787c4b4b7767e26a74f8adcfc40f23bd56eb0ea8c8b3a242a2aa5d215ac5dcd140a77fd534b70c61bb
|
7
|
+
data.tar.gz: cbc8e222372513f478cf809dc297e2b1b04511e48b00b9e498dc03d2cb5f72dca396c5cf4c57f49982b7a2697fdc59ad6c05ec388838d64e177c2cb1ad2c8c7b
|
data/README.md
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# hive-runner
|
2
|
+
|
3
|
+
Run automated jobs on devices
|
4
|
+
|
5
|
+
## Quick start
|
6
|
+
|
7
|
+
Install the hive-runner gem and set up your hive:
|
8
|
+
|
9
|
+
gem install hive-runner
|
10
|
+
hive_setup my_hive
|
11
|
+
|
12
|
+
Follow the configuration instructions and, in particular, ensure that the
|
13
|
+
`HIVE_CONFIG` variable is set.
|
14
|
+
|
15
|
+
Start the Hive daemon:
|
16
|
+
|
17
|
+
hived start
|
18
|
+
|
19
|
+
Determine the status of the Hive:
|
20
|
+
|
21
|
+
hived status
|
22
|
+
|
23
|
+
Stop the Hive:
|
24
|
+
|
25
|
+
hived stop
|
26
|
+
|
27
|
+
## Configuration file
|
28
|
+
|
29
|
+
Example config file:
|
30
|
+
|
31
|
+
test:
|
32
|
+
controllers:
|
33
|
+
shell:
|
34
|
+
max_workers: 5
|
35
|
+
name_stub: SHELL_WORKER
|
36
|
+
queues:
|
37
|
+
- bash
|
38
|
+
|
39
|
+
logging:
|
40
|
+
directory: logs
|
41
|
+
pids: pids
|
42
|
+
main_filename: hive.log
|
43
|
+
|
44
|
+
timings:
|
45
|
+
worker_loop_interval: 5
|
46
|
+
|
47
|
+
### Controllers
|
48
|
+
|
49
|
+
The `controllers` section contains details about the controllers to be
|
50
|
+
used by the hive. The name of each section indicates the controller type. Some
|
51
|
+
of the fields in each controllers section is common to all controller types
|
52
|
+
(see below) while some are defined for each specific controller type.
|
53
|
+
|
54
|
+
Fields for all controller types are:
|
55
|
+
|
56
|
+
| Field | Content |
|
57
|
+
|-------------------|-------------------------------------------|
|
58
|
+
| `max_workers` | Maximum number of workers to use |
|
59
|
+
| `port_range_size` | Number of ports to allocate to the device |
|
60
|
+
| `name_stub` | Stub for name of the worker process |
|
61
|
+
|
62
|
+
### Ports
|
63
|
+
|
64
|
+
| Field | Content |
|
65
|
+
|-----------|---------------------|
|
66
|
+
| `minimum` | Minimum port number |
|
67
|
+
| `maximum` | Maximum port number |
|
68
|
+
|
69
|
+
### Logging
|
70
|
+
|
71
|
+
| Field | Content |
|
72
|
+
|-----------------|---------------------------|
|
73
|
+
| `directory` | Log file directory |
|
74
|
+
| `pids` | PIDs directory |
|
75
|
+
| `main_filename` | Name of the main log file |
|
76
|
+
|
77
|
+
### Timings
|
78
|
+
|
79
|
+
The `worker_loop_interval` indicates the number of seconds to wait between each
|
80
|
+
poll of the job queue in the worker loop.
|
81
|
+
|
82
|
+
## Shell controller
|
83
|
+
|
84
|
+
### Configuration
|
85
|
+
|
86
|
+
The shell controller section contains the following additional field:
|
87
|
+
|
88
|
+
| Field | Content |
|
89
|
+
|-----------|-------------------------------------------|
|
90
|
+
| `queues` | Array of job queues for the shell workers |
|
91
|
+
| `workers` | Number of shell workers to run |
|
92
|
+
|
93
|
+
## Setting up a new Hive Runner
|
94
|
+
|
95
|
+
Check out the Hive Runner from Github:
|
96
|
+
|
97
|
+
# Using HTTPS
|
98
|
+
git clone https://github.com/bbc-test/hive-runner
|
99
|
+
# ... or using SSH
|
100
|
+
git clone ssh@github.com:bbc-test/hive-runner
|
101
|
+
# Ensure ruby gems are installed
|
102
|
+
cd hive-runner
|
103
|
+
bundle install
|
104
|
+
|
105
|
+
Configure the hive, either by editing the default configuration file,
|
106
|
+
`hive-runner/config/hive-runner.yml`, or creating a separate configuration
|
107
|
+
file in a separate location (recommended) and ensuring that the `HIVE_CONFIG`
|
108
|
+
environment variable is set correctly:
|
109
|
+
|
110
|
+
echo export HIVE_CONFIG=/path/to/hive-config-directory >> ~/.bashrc
|
111
|
+
|
112
|
+
See the "Configuration file" above for details.
|
113
|
+
|
114
|
+
Start the Hive Runner:
|
115
|
+
|
116
|
+
./bin/hived start
|
117
|
+
|
118
|
+
Ensure that your Hive Runner is running and that your workers have started:
|
119
|
+
|
120
|
+
./bin/hived status
|
data/bin/hive_setup
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'terminal-table'
|
6
|
+
|
7
|
+
if ARGV.length < 1
|
8
|
+
puts "Usage:"
|
9
|
+
puts
|
10
|
+
puts " hive_setup <directory>"
|
11
|
+
exit 1
|
12
|
+
end
|
13
|
+
|
14
|
+
dir = File.expand_path('', ARGV[0])
|
15
|
+
if Dir.exists?(dir)
|
16
|
+
if ! Dir["#{dir}/*"].empty?
|
17
|
+
puts "Directory '#{dir}' exists and is not empty"
|
18
|
+
exit 1
|
19
|
+
end
|
20
|
+
else
|
21
|
+
if File.exists?(dir)
|
22
|
+
puts "'#{dir}' exists and is not a directory"
|
23
|
+
exit 1
|
24
|
+
end
|
25
|
+
FileUtils.mkdir_p(dir)
|
26
|
+
end
|
27
|
+
|
28
|
+
def yn question
|
29
|
+
yn = 'x'
|
30
|
+
while yn !~ /^[yYnN]/
|
31
|
+
print "#{question}(y/n) "
|
32
|
+
yn = STDIN.gets.chomp
|
33
|
+
end
|
34
|
+
yn =~ /^[yY]/
|
35
|
+
end
|
36
|
+
|
37
|
+
# Choose options
|
38
|
+
opt = ''
|
39
|
+
mods = []
|
40
|
+
while opt.upcase != 'X'
|
41
|
+
table = Terminal::Table.new headings: ['Device', 'Module', 'Source']
|
42
|
+
mods.each do |mod|
|
43
|
+
table.add_row [
|
44
|
+
mod[:device],
|
45
|
+
mod[:name],
|
46
|
+
mod.has_key?(:git_account) ?
|
47
|
+
"git@github.com:#{mod[:git_account]}/#{mod[:name]}" :
|
48
|
+
"https://rubygems.org/gems/#{mod[:name]}"
|
49
|
+
]
|
50
|
+
end
|
51
|
+
|
52
|
+
puts ''
|
53
|
+
puts table
|
54
|
+
puts ''
|
55
|
+
puts '1) Add module'
|
56
|
+
puts 'X) Continue'
|
57
|
+
puts ''
|
58
|
+
print "> "
|
59
|
+
opt = STDIN.gets.chomp
|
60
|
+
|
61
|
+
case opt
|
62
|
+
when '1'
|
63
|
+
mod = {}
|
64
|
+
puts ''
|
65
|
+
print "Module name: "
|
66
|
+
mod[:device] = STDIN.gets.chomp
|
67
|
+
mod[:name] = "hive-runner-#{mod[:device]}"
|
68
|
+
puts ''
|
69
|
+
if yn "Get '#{mod[:name]}' from Github? "
|
70
|
+
print "Enter GIT account name: "
|
71
|
+
mod[:git_account] = STDIN.gets.chomp
|
72
|
+
end
|
73
|
+
|
74
|
+
puts ''
|
75
|
+
puts "Module '#{mod[:name]}'"
|
76
|
+
if mod.has_key?(:git_account)
|
77
|
+
puts " from git@github.com:#{mod[:git_account]}/#{mod[:name]}"
|
78
|
+
else
|
79
|
+
puts " from https://rubygems.org/gems/#{mod[:name]}"
|
80
|
+
end
|
81
|
+
puts ''
|
82
|
+
|
83
|
+
if yn "Correct? "
|
84
|
+
mods << mod
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
FileUtils.mkdir_p("#{dir}/config")
|
90
|
+
FileUtils.mkdir_p("#{dir}/log")
|
91
|
+
FileUtils.mkdir_p("#{dir}/pids")
|
92
|
+
FileUtils.mkdir_p("#{dir}/workspaces")
|
93
|
+
|
94
|
+
File.open("#{dir}/config/settings.yml", 'w') do |f|
|
95
|
+
f.puts "#{ENV['HIVE_ENVIRONMENT'] || 'test'}:"
|
96
|
+
f.puts ' daemon_name: HIVE'
|
97
|
+
f.puts ''
|
98
|
+
f.puts ' controllers:'
|
99
|
+
f.puts ' shell:'
|
100
|
+
f.puts ' # Number of shell workers to allocate'
|
101
|
+
f.puts ' workers: 5'
|
102
|
+
f.puts ' # Queue for each shell worker'
|
103
|
+
f.puts ' queues:'
|
104
|
+
f.puts ' - bash'
|
105
|
+
f.puts ' # Number of ports to allocate to each shell worker'
|
106
|
+
f.puts ' port_range_size: 50'
|
107
|
+
f.puts ' name_stub: SHELL_WORKER'
|
108
|
+
mods.each do |m|
|
109
|
+
f.puts " #{m[:device]}:"
|
110
|
+
f.puts ' # Number of ports to allocate to each #{m[:device]} worker'
|
111
|
+
f.puts ' port_range_size: 50'
|
112
|
+
f.puts " name_stub: #{m[:device].upcase}_WORKER"
|
113
|
+
end
|
114
|
+
f.puts ''
|
115
|
+
f.puts ' # Range of ports to be made available to workers'
|
116
|
+
f.puts ' ports:'
|
117
|
+
f.puts ' minimum: 4000'
|
118
|
+
f.puts ' maximum: 5000'
|
119
|
+
f.puts ''
|
120
|
+
f.puts ' # Logging configuration'
|
121
|
+
f.puts ' logging:'
|
122
|
+
f.puts " directory: #{dir}/log"
|
123
|
+
f.puts " pids: #{dir}/pids"
|
124
|
+
f.puts ' main_filename: hive.log'
|
125
|
+
f.puts ' main_level: INFO'
|
126
|
+
f.puts ' worker_level: INFO'
|
127
|
+
f.puts " home: #{dir}/workspaces"
|
128
|
+
f.puts ' homes_to_keep: 5'
|
129
|
+
f.puts ''
|
130
|
+
f.puts ' # Timing configuration'
|
131
|
+
f.puts ' timings:'
|
132
|
+
f.puts ' worker_loop_interval: 5'
|
133
|
+
f.puts ' controller_loop_interval: 5'
|
134
|
+
f.puts ''
|
135
|
+
f.puts ' # Configuration for various network options'
|
136
|
+
f.puts ' network:'
|
137
|
+
f.puts ' scheduler: https://scheduler'
|
138
|
+
f.puts ' devicedb: https://devicedb'
|
139
|
+
f.puts ' cert: /path/to/certificate.pem'
|
140
|
+
f.puts ' cafile: /path/to/certificate-authorities.pem'
|
141
|
+
f.puts ''
|
142
|
+
f.puts ' # Configuration for diagnostic plugins'
|
143
|
+
f.puts ' diagnostics:'
|
144
|
+
end
|
145
|
+
|
146
|
+
File.open("#{dir}/Gemfile", 'w') do |f|
|
147
|
+
f.puts "source 'https://rubygems.org/'"
|
148
|
+
f.puts ""
|
149
|
+
f.puts "gem 'hive-runner'"
|
150
|
+
mods.each do |m|
|
151
|
+
source = m.has_key?(:git_account) ? ", git: 'git@github.com:#{m[:git_account]}/#{m[:name]}'" : ''
|
152
|
+
f.puts "gem '#{m[:name]}'#{source}"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
print "Executing 'bundle install' ... "
|
157
|
+
Dir.chdir dir
|
158
|
+
if system("bundle install > bundle_install.out 2>&1")
|
159
|
+
print "SUCCESS\n"
|
160
|
+
File.delete('bundle_install.out')
|
161
|
+
else
|
162
|
+
print "FAILED\n"
|
163
|
+
puts "See #{dir}/bundle_install.out for details"
|
164
|
+
exit
|
165
|
+
end
|
166
|
+
|
167
|
+
puts ''
|
168
|
+
puts 'Configuration required:'
|
169
|
+
puts
|
170
|
+
puts ' * Add to config/settings.yml'
|
171
|
+
puts ' - scheduler'
|
172
|
+
puts ' - devicedb'
|
173
|
+
puts ' - cert'
|
174
|
+
puts ' - cafile'
|
175
|
+
if mods.length > 0
|
176
|
+
puts ' * Configure these modules in config/settings.yml'
|
177
|
+
mods.each do |m|
|
178
|
+
puts " - #{m[:device]}"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
puts ' * Add to ~/.bashrc'
|
182
|
+
puts " - export HIVE_CONFIG=#{dir}/config"
|
data/bin/hived
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path(
|
5
|
+
'../../Gemfile',
|
6
|
+
Pathname.new(__FILE__).realpath
|
7
|
+
)
|
8
|
+
|
9
|
+
require 'rubygems'
|
10
|
+
require 'bundler/setup'
|
11
|
+
require 'socket'
|
12
|
+
require 'daemons'
|
13
|
+
|
14
|
+
# require 'hive' here to cause a failure if the configuration is not correct
|
15
|
+
$LOAD_PATH << File.expand_path('../../lib', __FILE__)
|
16
|
+
require 'hive'
|
17
|
+
|
18
|
+
def test(app)
|
19
|
+
puts ''
|
20
|
+
app.default_show_status
|
21
|
+
|
22
|
+
Process.kill('USR1', app.pid.pid)
|
23
|
+
puts
|
24
|
+
server = TCPSocket.open('localhost', ENV.fetch('HIVE_COMM_PORT', 9999).to_i)
|
25
|
+
while line = server.gets
|
26
|
+
puts line.chop
|
27
|
+
end
|
28
|
+
server.close
|
29
|
+
end
|
30
|
+
|
31
|
+
Daemons.run(File.expand_path('..', __FILE__) + '/start_hive',
|
32
|
+
show_status_callback: :test,
|
33
|
+
log_dir: Hive.config.logging.directory,
|
34
|
+
log_output: true,
|
35
|
+
output_logfilename: 'hived.out')
|
data/bin/start_hive
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
puts "*** Starting Hive at #{Time.now} ***"
|
4
|
+
require 'pathname'
|
5
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path(
|
6
|
+
'../../Gemfile',
|
7
|
+
Pathname.new(__FILE__).realpath
|
8
|
+
)
|
9
|
+
|
10
|
+
$LOAD_PATH << File.expand_path('../../lib', __FILE__)
|
11
|
+
|
12
|
+
require 'socket'
|
13
|
+
require 'terminal-table'
|
14
|
+
require 'hive'
|
15
|
+
|
16
|
+
$PROGRAM_NAME = Hive::DAEMON_NAME
|
17
|
+
|
18
|
+
# Version information
|
19
|
+
gems = {}
|
20
|
+
Gem::Specification.find_all_by_name(/hive-*/).each do |g|
|
21
|
+
gems[g.name] = g.version
|
22
|
+
end
|
23
|
+
|
24
|
+
# Communication with daemon launcher
|
25
|
+
comm_port = ENV.fetch('HIVE_COMM_PORT', 9999).to_i
|
26
|
+
comm = TCPServer.open(comm_port)
|
27
|
+
Signal.trap('USR1') do
|
28
|
+
client = comm.accept
|
29
|
+
client.puts " Controllers in use:"
|
30
|
+
Hive.register.controllers.each do |c|
|
31
|
+
client.puts " * #{c.class.name.split('::').last.downcase}"
|
32
|
+
end
|
33
|
+
|
34
|
+
client.puts
|
35
|
+
|
36
|
+
client.puts " Software versions:"
|
37
|
+
client.puts " * hive-runner: #{gems['hive-runner']}"
|
38
|
+
gems.except('hive-runner').sort.each do |name, version|
|
39
|
+
client.puts " * #{name}: #{version}"
|
40
|
+
end
|
41
|
+
|
42
|
+
client.puts
|
43
|
+
|
44
|
+
# Create a hash of connected device details
|
45
|
+
devices = Hive.register.devices
|
46
|
+
device_details = {}
|
47
|
+
client.puts " Total number of devices: #{devices.length}"
|
48
|
+
if devices.length > 0
|
49
|
+
devices.each do |d|
|
50
|
+
device_details[d.identity] = { worker: d.claimed? ? 'Claimed' : d.worker_pid }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
client.puts ''
|
54
|
+
|
55
|
+
# Collect up the queue information per device/worker
|
56
|
+
device_details.each do |d, details|
|
57
|
+
if pid = details[:worker]
|
58
|
+
begin
|
59
|
+
queue_file = "#{Hive.config.logging.directory}/#{pid}.queues.yml"
|
60
|
+
details[:queues] = YAML.load( File.open(queue_file) )
|
61
|
+
rescue
|
62
|
+
details[:queues] = [ "---" ]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Get a seperate hash of workspace details
|
68
|
+
workspaces_list = {}
|
69
|
+
Dir["#{Hive.config.logging.home}/*"].each do |d|
|
70
|
+
if File.directory?(d)
|
71
|
+
if File.exists?("#{d}/job_info")
|
72
|
+
File.open("#{d}/job_info") do |f|
|
73
|
+
if f.read =~ /^(\d*)\s*(\S*)$/
|
74
|
+
worker = $1 || '?'
|
75
|
+
state = $2 || '?'
|
76
|
+
if workspaces_list.has_key?(worker)
|
77
|
+
workspaces_list[worker][File.basename(d)] = state
|
78
|
+
else
|
79
|
+
workspaces_list[worker] = {File.basename(d) => state}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Build the table from all that data just collected
|
88
|
+
table = Terminal::Table.new headings: ['Device', 'Worker', 'Job', 'Status', 'Queues']
|
89
|
+
device_details.each do |d, details|
|
90
|
+
table.add_separator
|
91
|
+
jobs = states = [ '---' ]
|
92
|
+
if workspaces_list.has_key?(details[:worker].to_s)
|
93
|
+
jobs = workspaces_list[details[:worker].to_s].keys
|
94
|
+
states = jobs.map { |key| workspaces_list[details[:worker].to_s][key] }
|
95
|
+
workspaces_list.delete(details[:worker].to_s)
|
96
|
+
end
|
97
|
+
table.add_row [d, details[:worker], jobs.join("\n"), states.join("\n"), details[:queues].join("\n")]
|
98
|
+
end
|
99
|
+
workspaces_list.each do |worker, list|
|
100
|
+
table.add_separator
|
101
|
+
col1 = '---'
|
102
|
+
col2 = worker
|
103
|
+
col5 = '---'
|
104
|
+
list.each do |job, state|
|
105
|
+
table.add_row [col1, col2, job, state, col5]
|
106
|
+
col1 = col2 = ''
|
107
|
+
end
|
108
|
+
end
|
109
|
+
client.puts table
|
110
|
+
|
111
|
+
client.close
|
112
|
+
end
|
113
|
+
|
114
|
+
# Initialise
|
115
|
+
Hive.register.instantiate_controllers
|
116
|
+
|
117
|
+
# Execution loop
|
118
|
+
Hive::logger.info('*** HIVE STARTING ***')
|
119
|
+
Hive::logger.info('Starting execution loop')
|
120
|
+
Hive.register.run
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'hive/controller'
|
2
|
+
require 'hive/worker/shell'
|
3
|
+
|
4
|
+
module Hive
|
5
|
+
class Controller
|
6
|
+
# The Shell controller
|
7
|
+
class Shell < Controller
|
8
|
+
def initialize(options)
|
9
|
+
Hive.logger.debug("options: #{options.inspect}")
|
10
|
+
@workers = options['workers'] || 0
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def detect
|
15
|
+
Hive.logger.info('Creating shell devices')
|
16
|
+
(1..@workers).collect do |i|
|
17
|
+
Hive.logger.info(" Shell device #{i}")
|
18
|
+
self.create_device('id' => i)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'hive'
|
2
|
+
|
3
|
+
module Hive
|
4
|
+
# Generic hive controller class
|
5
|
+
class Controller
|
6
|
+
attr_reader :port_range_size
|
7
|
+
|
8
|
+
class DeviceDetectionFailed < StandardError
|
9
|
+
end
|
10
|
+
|
11
|
+
class NoPortsAvailable < StandardError
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(config = {})
|
15
|
+
@config = config
|
16
|
+
@device_class = self.class.to_s.sub('Controller', 'Device')
|
17
|
+
require @device_class.downcase.gsub(/::/, '/')
|
18
|
+
Hive.logger.info("Controller '#{self.class}' created")
|
19
|
+
@port_range_size = (@config.has_key?('port_range_size') ? @config['port_range_size'] : 0)
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_device(extra_options = {})
|
23
|
+
object = Object
|
24
|
+
@device_class.split('::').each { |sub| object = object.const_get(sub) }
|
25
|
+
object.new(@config.merge(extra_options))
|
26
|
+
end
|
27
|
+
|
28
|
+
def detect
|
29
|
+
raise NotImplementedError, "'detect' method not defined for '#{self.class}'"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/hive/device.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'hive'
|
2
|
+
require 'hive/port_allocator'
|
3
|
+
|
4
|
+
module Hive
|
5
|
+
# The generic device class
|
6
|
+
class Device
|
7
|
+
attr_reader :type
|
8
|
+
attr_accessor :status
|
9
|
+
attr_accessor :port_allocator
|
10
|
+
|
11
|
+
# Initialise the device
|
12
|
+
def initialize(options)
|
13
|
+
@worker_pid = nil
|
14
|
+
@options = options
|
15
|
+
@port_allocator = options['port_allocator'] or Hive::PortAllocator.new(ports: [])
|
16
|
+
@status = @options.has_key?('status') ? @options['status'] : 'none'
|
17
|
+
@worker_class = self.class.to_s.sub('Device', 'Worker')
|
18
|
+
require @worker_class.downcase.gsub(/::/, '/')
|
19
|
+
raise ArgumentError, "Identity not set for #{self.class} device" if ! @identity
|
20
|
+
end
|
21
|
+
|
22
|
+
# Start the worker process
|
23
|
+
def start
|
24
|
+
parent_pid = Process.pid
|
25
|
+
@worker_pid = Process.fork do
|
26
|
+
object = Object
|
27
|
+
@worker_class.split('::').each { |sub| object = object.const_get(sub) }
|
28
|
+
object.new(@options.merge('parent_pid' => parent_pid, 'device_identity' => self.identity, 'port_allocator' => self.port_allocator))
|
29
|
+
end
|
30
|
+
Process.detach @worker_pid
|
31
|
+
|
32
|
+
Hive.logger.info("Worker started with pid #{@worker_pid}")
|
33
|
+
end
|
34
|
+
|
35
|
+
# Terminate the worker process
|
36
|
+
def stop
|
37
|
+
begin
|
38
|
+
count = 0
|
39
|
+
while self.running? && count < 30 do
|
40
|
+
count += 1
|
41
|
+
Hive.logger.info("Attempting to terminate process #{@worker_pid} [#{count}]")
|
42
|
+
Process.kill 'TERM', @worker_pid
|
43
|
+
sleep 30
|
44
|
+
end
|
45
|
+
Process.kill 'KILL', @worker_pid if self.running?
|
46
|
+
rescue => e
|
47
|
+
Hive.logger.info("Process had already terminated")
|
48
|
+
end
|
49
|
+
@worker_pid = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
# Test the state of the worker process
|
53
|
+
def running?
|
54
|
+
if @worker_pid
|
55
|
+
begin
|
56
|
+
Process.kill 0, @worker_pid
|
57
|
+
true
|
58
|
+
rescue Errno::ESRCH
|
59
|
+
false
|
60
|
+
end
|
61
|
+
else
|
62
|
+
false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Return the worker pid, checking to see if it is running first
|
67
|
+
def worker_pid
|
68
|
+
@worker_pid = nil if ! self.running?
|
69
|
+
@worker_pid
|
70
|
+
end
|
71
|
+
|
72
|
+
# Return true if the device is claimed
|
73
|
+
# If the device has no status set it is assumed not to be claimed
|
74
|
+
def claimed?
|
75
|
+
@status == 'claimed'
|
76
|
+
end
|
77
|
+
|
78
|
+
# Test equality with another device
|
79
|
+
def ==(other)
|
80
|
+
self.identity == other.identity
|
81
|
+
end
|
82
|
+
|
83
|
+
# Return the unique identity of the device
|
84
|
+
def identity
|
85
|
+
"#{self.class.to_s.split('::').last}-#{@identity}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'hive'
|
2
|
+
require 'hive/results'
|
3
|
+
|
4
|
+
module Hive
|
5
|
+
class Diagnostic
|
6
|
+
|
7
|
+
class InvalidParameterError < StandardError
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :last_run
|
11
|
+
attr_reader :config, :device_api
|
12
|
+
|
13
|
+
def initialize(config, options)
|
14
|
+
@options = options
|
15
|
+
@config = config
|
16
|
+
@serial = @options['serial']
|
17
|
+
@device_api = @options['device_api']
|
18
|
+
end
|
19
|
+
|
20
|
+
def should_run?
|
21
|
+
return true if @last_run == nil
|
22
|
+
time_now = Time.new.getutc
|
23
|
+
last_run_time = @last_run.timestamp
|
24
|
+
diff = ((time_now - last_run_time)/300).round
|
25
|
+
if (diff > 2 && @last_run.passed?) || diff > 1
|
26
|
+
true
|
27
|
+
else
|
28
|
+
false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def run
|
33
|
+
Hive.logger.info("Trying to run diagnostic '#{self.class}'")
|
34
|
+
if should_run?
|
35
|
+
result = diagnose
|
36
|
+
result = repair(result) if result.failed?
|
37
|
+
@last_run = result
|
38
|
+
else
|
39
|
+
Hive.logger.info("Diagnostic '#{self.class}' last ran less than five minutes before")
|
40
|
+
end
|
41
|
+
@last_run
|
42
|
+
end
|
43
|
+
|
44
|
+
def pass(message= {}, data = {})
|
45
|
+
Hive.logger.info(message)
|
46
|
+
Hive::Results.new("pass", message, data )
|
47
|
+
end
|
48
|
+
|
49
|
+
def fail(message ={}, data = {})
|
50
|
+
Hive.logger.info(message)
|
51
|
+
Hive::Results.new("fail", message, data)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|